УПП, формирование записей книги продаж: ускоряем заполнение при большом объеме реализаций

28.07.17

База данных - HighLoad оптимизация

В конце апреля 2017 года ко мне обратилась бухгалтер по НДС с жалобой на очень длительное заполнение документа "Формирование записей книги продаж" по реализации. По ее словам, заполнение документа могло продолжаться от 40 минут до часа за месяц... Разбираемся в ситуации.

Итак, дана находящаяся в актуальном состоянии клиент-серверная УПП. Квартальный объем документов реализации у нас приличный, но далеко не рекордный — примерно около 40 тыс. реализаций товаров и услуг, правда, с тенденцией к увеличению. Пришлось открыть конфигуратор и взяться за изучение 1Сного кода. Результатами решил поделиться с сообществом, потому что вполне может быть, что у кого-то возникнет та же проблема либо в УПП, либо в УТ 10, где этот документ устроен аналогично.

Вскрытие показало, что грабли ожидали нас в модуле документа ФормированиеЗаписейКнигиПродаж, а более конкретно - в процедуре РаспределитьОплатыПоДеревуСФ. В данной процедуре выполняется отбор из таблицы РаспределенныеОплаты с помощью построителя запроса. Отбор несложный, но используются операции на больше/меньше:

Отбор = Построитель_РаспределенныеОплаты.Отбор;
Отбор.Добавить("СчетФактура");
Отбор.СчетФактура.Использование = Истина;
Отбор.Добавить("РаспределеннаяОплата");
Отбор.РаспределеннаяОплата.ВидСравнения = ВидСравнения.Больше;
Отбор.РаспределеннаяОплата.Значение = 0;
Отбор.РаспределеннаяОплата.Использование = Истина;

Таблица РаспределенныеОплаты в базах с большим объемом реализаций обычно не совсем мала — в моем случае она имела на март месяц пару десятков тысяч строк. Но главная проблема даже не в этом, а в том, что в дальнейшем этот отбор делается многократно в цикле с большим количеством итераций:

Для каждого СтрокаСФ Из Дерево_НДСНачисленный.Строки Цикл

НаличиеОплатыНеТребуется = (СтрокаСФ.Строки[0].МоментОпределенияНалоговойБазыНДС = МоментОпределения_ПоОтгрузке) Или Дата >= '20080101';
ТаблицаОплат.Очистить();
Отбор = Построитель_РаспределенныеОплаты.Отбор;
Отбор.СчетФактура.Значение = СтрокаСФ.СчетФактура;
Отбор.РаспределеннаяОплата.ВидСравнения = ?(СтрокаСФ.СуммаСНДС>0,ВидСравнения.Больше,ВидСравнения.Меньше);

Построитель_РаспределенныеОплаты.Выполнить();

Собственно, вот здесь и проявляются искомые «тормоза» - этот циклический фрагмент мог гоняться от получаса и дольше в зависимости от количества документов в течение месяца.

Сначала я попробовал решить вопрос «малой кровью», проиндексировав таблицу РаспределенныеОплаты, но совместно с построителем запроса никакого эффекта это не дало. Тогда было решено отказаться от построителя и переписать механизм с использованием метода НайтиСтроки. Предлагаю вашему вниманию переписанную часть процедуры с моими комментариями.

Процедура РаспределитьОплатыПоДеревуСФ(Дерево_НДСНачисленный, ТаблицаРезультатов, СписокСчетовФактур, РаспределенныеОплаты, ОтражатьВРеестре = Истина, ОтражатьВидНачисления = Ложь )

НДСНалоговыйПериод = Неопределено;

// Далее используются следующие комментарии
// [-] и дальнейшее комментирование - исходный фрагмент удален
// [+] {...} - новый фрагмент вставлен

// [-] VVP - удаляем подготовку построителя и его отбора
//Построитель_РаспределенныеОплаты = Новый построительЗапроса();
//Построитель_РаспределенныеОплаты.ИсточникДанных = Новый ОписаниеИсточникаДанных(РаспределенныеОплаты);
//
//// Подготовка структуры отбора
//Отбор = Построитель_РаспределенныеОплаты.Отбор;
//Отбор.Добавить("СчетФактура");
//Отбор.СчетФактура.Использование = Истина;
//Отбор.Добавить("РаспределеннаяОплата");
//Отбор.РаспределеннаяОплата.ВидСравнения = ВидСравнения.Больше;
//Отбор.РаспределеннаяОплата.Значение = 0;
//Отбор.РаспределеннаяОплата.Использование = Истина;
//
//Построитель_РаспределенныеОплаты.Порядок.Добавить("ДатаОплаты");

ТаблицаОплат = Новый ТаблицаЗначений();
ТаблицаОплат.Колонки.Добавить("ДокументОплаты");
ТаблицаОплат.Колонки.Добавить("ДатаОплаты",Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя)));
ТаблицаОплат.Колонки.Добавить("СуммаОплаты",Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15,2)));

// [-] VVP - удаляем ссылку на таблицу источника построителя
//ТаблицаИсточникаПостроителя = Построитель_РаспределенныеОплаты.ИсточникДанных.ИсточникДанных;

// [+] VVP - делаем свою "таблицу источника построителя" и индексируем ее {
ТаблицаИсточникаПостроителя = РаспределенныеОплаты.Скопировать ();
ВыборкаОплат = РаспределенныеОплаты.СкопироватьКолонки ();
ТаблицаИсточникаПостроителя.Индексы.Добавить ("СчетФактура");
// }

МоментОпределения_ПоОтгрузке = Перечисления.МоментыОпределенияНалоговойБазыНДС.ПоОтгрузке;

Для каждого СтрокаСФ Из Дерево_НДСНачисленный.Строки Цикл

ТаблицаОплат.Очистить();

Если УчетНДС.ДляСчетаФактурыНеТребуетсяОплата(СтрокаСФ.СчетФактура) Тогда

НаличиеОплатыНеТребуется = Истина;

Иначе

НаличиеОплатыНеТребуется = (СтрокаСФ.Строки[0].МоментОпределенияНалоговойБазыНДС = МоментОпределения_ПоОтгрузке) Или Дата >= '20080101';

// [-] VVP - удаляем запрос построителем
//Отбор = Построитель_РаспределенныеОплаты.Отбор;
//Отбор.СчетФактура.Значение = СтрокаСФ.СчетФактура;
//Отбор.РаспределеннаяОплата.ВидСравнения = ?(СтрокаСФ.СуммаСНДС>0,ВидСравнения.Больше,ВидСравнения.Меньше);
//
//Построитель_РаспределенныеОплаты.Выполнить();
//Если Построитель_РаспределенныеОплаты.Результат.Пустой() и не НаличиеОплатыНеТребуется и СтрокаСФ.СуммаСНДС >= 0 Тогда
// // Оплата не обнаружена
// Продолжить;
//КонецЕсли;
//
//ВыборкаОплат = Построитель_РаспределенныеОплаты.Результат.Выгрузить(ОбходРезультатаЗапроса.Прямой);

// [+] VVP - делаем свой "запрос" данных через НайтиСтроки и далее навигацией по строкам {

МассивСтрокОплатПоСФ = ТаблицаИсточникаПостроителя.НайтиСтроки (Новый Структура ("СчетФактура", СтрокаСФ.СчетФактура));
ВыборкаОплат.Очистить ();
Для Каждого СтрокаОплатыПоСФ Из МассивСтрокОплатПоСФ Цикл

Если ((СтрокаСФ.СуммаСНДС > 0) И (СтрокаОплатыПоСФ.РаспределеннаяОплата > 0)) или
((СтрокаСФ.СуммаСНДС <= 0) И (СтрокаОплатыПоСФ.РаспределеннаяОплата < 0)) Тогда

СтрокаВыборкиОплат = ВыборкаОплат.Добавить ();

ЗаполнитьЗначенияСвойств(СтрокаВыборкиОплат, СтрокаОплатыПоСФ);

КонецЕсли;

КонецЦикла;

ВыборкаОплат.Сортировать ("ДатаОплаты");

Если ВыборкаОплат.Количество()=0 и не НаличиеОплатыНеТребуется и СтрокаСФ.СуммаСНДС >= 0 Тогда

// Оплата не обнаружена
Продолжить;

КонецЕсли;

// }

СуммаКПогашению = СтрокаСФ.СуммаСНДС;

В 

... и далее код от 1С без изменений.

Добавлю, что индексация таблицы по колонке СчетФактура в данном случае несколько увеличивает скорость поиска, но несущественно, а вот отказ от построителя в пользу метода НайтиСтроки дает огромную прибавку в скорости даже на неиндексированной таблице. В случае с моей УПП скорость выполнения процедуры без индексации таблицы увеличилась примерно в 10 раз — с 40 минут до 4, а с индексацией удалось добиться примерно 3 минут вместо 4. В конечном итоге я все же оставил индексацию таблицы в рабочем коде.

См. также

SALE! 15%

Автоматический заказ поставщику в 1С: загрузка прайсов и анализ цен поставщиков для УТ 10.3, УТ 11, КА2, УНФ, УПП, ERP, Розница 2

Бюджетирование и планирование Оптовая торговля Розничная торговля Логистика, склад и ТМЦ Анализ продаж Платформа 1С v7.7 Платформа 1С v8.3 1С:Комплексная автоматизация 1.х 1С:Управление торговлей 10 1С:Розница 2 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Розничная и сетевая торговля (FMCG) Оптовая торговля, дистрибуция, логистика Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Система управления запасами для 1С помогает работать с запасами правильно: автоматически рассчитывает потребность и делает заказ поставщику, загружает прайсы, перемещает товары по филиалам, анализирует продажи и позволяет управлять ассортиментом.

33529 28500 руб.

21.04.2017    89858    105    39    

188

ЕГАИС++. Опт, производство, импорт

Оптовая торговля Розничная торговля Обмен с ГосИС Платформа 1С v8.3 1С:Управление торговлей 10 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 Розничная и сетевая торговля (FMCG) Оптовая торговля, дистрибуция, логистика Рестораны, кафе и фаст-фуд Россия Бухгалтерский учет Управленческий учет Акцизы Платные (руб)

Полнофункциональное расширение (ранее известное как Модуль 1С-ЕГАИС) для взаимодействия типовых конфигураций 1С и ЕГАИС, предоставляющее максимум возможностей по работе с УТМ. Получение и отправка ТТН, отправка акта о постановке на баланс и акта о списании. Получение остатков. Загрузка и сопоставление номенклатуры и контрагентов. Оправка в ЕГАИС отчетов о производстве и импорте.

8970 руб.

15.12.2015    165790    673    361    

385

SALE! 10%

Перенос данных из УТ 10.3 в УТ 11 / КА 2 / ERP 2. Переносятся документы, справочники и остатки

Обмен между базами 1C Взаиморасчеты Оптовая торговля Логистика, склад и ТМЦ Файловый обмен (TXT, XML, DBF), FTP Платформа 1С v8.3 1С:Управление торговлей 10 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Управленческий учет Платные (руб)

Предлагаем качественное и проверенное временем решение для перехода с УТ 10.3 на УТ 11 / КА 2 / ERP 2. Перенос данных находится в продаже с 2015 года, постоянно развивается, им воспользовались уже более 240 компаний. Можно перенести начальные остатки, нормативно-справочную информацию и все возможные документы. При выгрузке можно установить отбор по периоду, организациям и складам. При выходе новых релизов конфигураций 1C оперативно выпускаем обновление переноса данных.

50722 45650 руб.

24.04.2015    190284    268    238    

268

Обмен с системой ЦРПТ (Универсальная конфигурация ХамелеонЦРПТ + маркировка табака, обуви, одежды, лекарств, фото, молока, духов(парфюма), питьевой воды, велосипедов и шин)

Оптовая торговля Розничная торговля Обмен с ГосИС Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Розничная и сетевая торговля (FMCG) Оптовая торговля, дистрибуция, логистика Россия Бухгалтерский учет Управленческий учет Платные (руб)

Данная публикация создана для помощи разработчикам, интеграторам и другим заинтересованным лицам по настройке системы маркировки обуви, одежды, лекарств, табака, фото, молока, духов(парфюма), питьевой воды, велосипедов и шин. Смело задавайте нам вопросы по работе с ЦРПТ, GS1, ЭДО, Национальным каталогом, мы накопили достаточно большую базу знаний по данным темам и готовы ответить на все Ваши вопросы.

104000 руб.

18.03.2019    110207    33    114    

177

Простое ценообразование (установка цен номенклатуры) для 1С 8.3 (УТ 11 / ERP 2 / КА 2 / Розница 2) + (УТ 10.3 / УПП / КА 1 / Розница 1)

Оптовая торговля Розничная торговля Ценообразование, анализ цен Платформа 1С v8.3 Управляемые формы 1С:Комплексная автоматизация 1.х 1С:Управление торговлей 10 1С:Розница 2 1С:Управление производственным предприятием 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:CRM ПРОФ, КОРП Управленческий учет Платные (руб)

Есть проблемы с расчетом и установкой цен на товары? Универсальная подсистема для ценообразования в 1С поможет навести порядок с ценами! Механизм позволяет задавать произвольные правила расчета колонок цен для разных групп товаров и легко их изменять. Может автоматически (по расписанию) обновлять цены в 1С и выполнять проверку наличия и корректности цен на все товары.

30000 руб.

13.11.2017    77828    40    11    

46
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. ushakovdv 02.05.17 10:27 Сейчас в теме
Спасибо! 20000 строк заполняет за 16 секунд!
2. spectre1978 60 04.05.17 13:10 Сейчас в теме
(1) Рад, что оказалось полезным.
3. vis_tmp 32 14.05.17 20:11 Сейчас в теме
Зачем разработчики сделали это на построителе?...
4. spectre1978 60 14.05.17 22:47 Сейчас в теме
(3) при наличии сложных условий на построителе код, конечно, красивее и читабельнее. Только скорость оставляет желать сильно много лучшего.
5. Lusha_28 47 26.07.17 08:43 Сейчас в теме
Большое спасибо за вашу статью, очень круто. У нас 40-50 тыс. реализаций и возвратов в месяц, формирование записей книги продаж было -30-40мин., теперь -2-3минуты.
И маленький вопрос:
можно конструкцию
СтрокаВыборкиОплат = ВыборкаОплат.Добавить ();

Для каждого Колонка из ВыборкаОплат.Колонки Цикл

ИндексКолонки = ВыборкаОплат.Колонки.Индекс (Колонка);
СтрокаВыборкиОплат.Установить(ИндексКолонки, СтрокаОплатыПоСФ.Получить (ИндексКолонки));

КонецЦикла;
Показать

заменить на
СтрокаВыборкиОплат = ВыборкаОплат.Добавить ();
ЗаполнитьЗначенияСвойств(СтрокаВыборкиОплат, СтрокаОплатыПоСФ);
?
spectre1978; +1 Ответить
6. spectre1978 60 26.07.17 09:09 Сейчас в теме
(5)да, конечно, и это будет правильно
7. spectre1978 60 28.07.17 08:35 Сейчас в теме
Внес изменение в публикацию в соответствии с заметкой в (5). Спасибо за уточнение!
8. Repey 17.04.20 15:06 Сейчас в теме
Приветствую.

Благодарю, воспользовался решением.
Однако не понятно - для чего тут нужна ТаблицаОплат?
9. spectre1978 60 17.04.20 15:20 Сейчас в теме
(8) Здравствуйте, рад что помогло в работе. По вашему вопросу:
Однако не понятно - для чего тут нужна ТаблицаОплат?

Обратите, пожалуйста, внимание, что в приведенном фрагменте с изменениями цикл верхнего уровня
Для каждого СтрокаСФ Из Дерево_НДСНачисленный.Строки Цикл

и сама процедура
Процедура РаспределитьОплатыПоДеревуСФ(Дерево_НДСНачисленный, ТаблицаРезультатов, СписокСчетовФактур, РаспределенныеОплаты, ОтражатьВРеестре = Истина, ОтражатьВидНачисления = Ложь )

не завершены.
Ниже, уже по коду от 1С из вашего модуля, без моих изменений, данная ТЗ используется.
Оставьте свое сообщение