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

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. В конечном итоге я все же оставил индексацию таблицы в рабочем коде.

См. также

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

Автоматический обмен данными с системой ФГИС Меркурий из 1С через ВетИС API: загрузка данных по хозяйствующим субъектам, предприятиям; типов, групп, видов, наименований продукции, сопоставление данных и запись их в базу данных; создание на основании расходных документов транспортных партий, отправка на сервер, печать полученных ветеринарных свидетельств, запрос остатков складского журнала, проведение инвентаризаций, оформление производственных партий.

11500 руб.

08.12.2017    106176    291    110    

147

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

Автоматизация учета ЕГАИС в 1С для оптовой торговли, производства и импорта. Получение и отправка ТТН, отправка акта о постановке на баланс и акта о списании. Получение остатков. Загрузка и сопоставление номенклатуры и контрагентов. Оправка в ЕГАИС отчетов о производстве и импорте.

828 руб.

15.12.2015    172765    865    370    

407

SALE! 15%

Загрузка и выгрузка в Excel Оптовая торговля Печатные формы Бухгалтер Пользователь Платформа 1С v8.3 Управляемые формы Платформа 1C v8.2 1C:Бухгалтерия 1С:Комплексная автоматизация 1.х 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Розница 2 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Управление холдингом 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Бухгалтерский учет Управленческий учет Платные (руб)

Универсальная обработка для загрузки документов из Excel в 1С. Забудьте о ручном вводе: загружайте документы из Excel в 1С за секунды! Не требует указания параметров (номера колонок, номер первой строки таблицы и т.д.) и предварительной настройки. Просто выбираете файл Excel, документ 1С и нажимаете кнопку "Загрузить". Обработка сама находит таблицу в файле Excel, необходимые для загрузки данные в ней (номенклатура, количество, НДС, цена, сумма) и загружает ее в 1С.

6000 5100 руб.

09.11.2016    242694    1117    910    

1046

Оптовая торговля Розничная торговля Пользователь Платформа 1С v8.3 Оперативный учет Управляемые формы 1С:Управление торговлей 10 1С:Розница 2 Россия Управленческий учет Платные (руб)

Подсистема призвана упростить и автоматизировать процесс расчета и начисления бонусов покупателей. Бонусная система работает с конфигурациями только 1С:УТ 10.3. Механизм реализован в начале 2013г. и работает до сих пор более 100 клиентов по РФ.

250000 руб.

02.11.2015    113933    6    89    

187

SALE! 50%

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

Данный отчет показывает себестоимость выпущенной продукции с разузлованием полуфабрикатов любых уровней, входящих в ее состав, до статей затрат и материалов. Отчет работает независимо от метода оценки стоимости товаров и подходит для любых производственных компаний с многопередельным производством. Отчет можно использовать как в типовой 1С:ERP, так и в отраслевых решениях на ее базе (например, 1С:ERP Управление птицеводческим предприятием и т.д.). Отчет подходит для анализа затрат на гособоронзаказы ГОЗ.

84000 42000 руб.

08.12.2021    28741    31    74    

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

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

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

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

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

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

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

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

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