Часто перед разработчиками 1С встает задача доработать стандартные формы списков, добавив в них вычисляемые данные. Классический пример — отображение итоговой суммы документа по его движениям в регистрах. Такой функционал очень удобен для пользователей, но при неправильной реализации может привести к катастрофическому падению производительности, когда форма списка открывается минутами.
Рассмотрим на конкретном примере, как добавить колонку с суммой проводок в список документов "Требование-накладная" в конфигурации "1С:Бухгалтерия 3.0", и разберем, как избежать "тормозов" при работе с динамическим списком и виртуальными таблицами регистров.
Исходная задача — в форму списка документов Требование-накладная добавить колонку "Сумма", которая будет показывать итог по кредитовым оборотам проводок, сформированных документом. Для этого был изменен запрос динамического списка. Сначала во временную таблицу собирались обороты, а затем она присоединялась к основной таблице документов.
Давайте посмотрим на первоначальный вариант запроса для временной таблицы:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ХозрасчетныйОбороты.Регистратор КАК ТребованиеНакладная,
СУММА(ЕСТЬNULL(ХозрасчетныйОбороты.СуммаОборотКт, 0)) КАК СуммаОборотКт
ПОМЕСТИТЬ ВТ_Обороты
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(&ДатаДокумента, &ДатаДокумента, Регистратор, ) КАК ХозрасчетныйОбороты
СГРУППИРОВАТЬ ПО
ХозрасчетныйОбороты.Регистратор
ИНДЕКСИРОВАТЬ ПО
ТребованиеНакладная
На первый взгляд, все логично. Мы берем обороты из регистра бухгалтерии, группируем их по регистратору и получаем нужную сумму. Однако именно в этом фрагменте кроется корень проблемы производительности. Проанализируем, что на самом деле делает этот запрос:
Регистратор, в параметрах виртуальной таблицы означает, что система будет анализировать движения, сделанные абсолютно всеми документами в базе, которые делают проводки по регистру Хозрасчетный. Это могут быть поступления, реализации, списания и т.д. Объем данных огромен.Обороты() не указаны конкретные счета или адекватный период. Из-за этого система пытается поднять данные за весь период существования информационной базы по всем счетам.В итоге, при каждом открытии или обновлении формы списка система вынуждена была пересчитывать гигантскую временную таблицу, что и приводило к длительным задержкам.
Это самый быстрый способ решения проблемы, который не требует создания новых объектов метаданных. Идея заключается в том, чтобы максимально "сузить" выборку для виртуальной таблицы оборотов, добавив в нее жесткие условия. Проанализируем шаги по оптимизации.
Шаг 1. Ограничение по типу регистратора.
Самое главное — указать системе, что нас интересуют движения только одного вида документов. Для этого в запрос к виртуальной таблице нужно добавить условие ГДЕ с проверкой типа регистратора.
Шаг 2. Ограничение по счетам.
Если вы знаете, по каким конкретно счетам делает движения документ (например, по счетам учета материалов из табличной части), обязательно добавьте фильтр по ним. Это еще сильнее сократит объем данных для анализа.
С учетом этих правок, оптимизированный запрос для временной таблицы будет выглядеть так:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ХозрасчетныйОбороты.Регистратор КАК ТребованиеНакладная,
СУММА(ЕСТЬNULL(ХозрасчетныйОбороты.СуммаОборотКт, 0)) КАК СуммаОборотКт
ПОМЕСТИТЬ ВТ_Обороты
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(, , Регистратор, ) КАК ХозрасчетныйОбороты
// НАЧАЛО ИЗМЕНЕНИЙ
ГДЕ
ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ТребованиеНакладная
// И, если возможно, добавляем отбор по счетам
// И ХозрасчетныйОбороты.СчетКт В (&СписокСчетов)
// КОНЕЦ ИЗМЕНЕНИЙ
СГРУППИРОВАТЬ ПО
ХозрасчетныйОбороты.Регистратор
ИНДЕКСИРОВАТЬ ПО
ТребованиеНакладная
После внесения этих изменений запрос будет выбирать данные только по нужным документам, и скорость работы формы списка возрастет в десятки, а то и сотни раз. Этого простого действия оказалось достаточно для решения исходной проблемы автора.
Хотя оптимизация запроса решает проблему "здесь и сейчас", для высоконагруженных систем с большим документооборотом даже такой запрос может со временем начать замедляться. Более надежным и масштабируемым решением является предварительный расчет и хранение нужных данных.
Разберем этот подход по шагам:
Регистр сведений, назовем его, например, СуммыПроводокДокументов.
ДокументСсылка (тип ДокументСсылка.ТребованиеНакладная, ведущее).СуммаДокумента (тип Число).ОбработкаПроведения для документа ТребованиеНакладная. В обработчике этой подписки пишется код, который:
Требование-накладная, рассчитывает для них суммы и заполняет новый регистр сведений. Эту обработку нужно запустить один раз.ЛЕВОЕ СОЕДИНЕНИЕ с нашим новым, легким регистром сведений.
ВЫБРАТЬ
ДокументТребованиеНакладная.Ссылка,
...
ЕСТЬNULL(СуммыПроводок.СуммаДокумента, 0) КАК СуммаДокумента
ИЗ
Документ.ТребованиеНакладная КАК ДокументТребованиеНакладная
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СуммыПроводокДокументов КАК СуммыПроводок
ПО ДокументТребованиеНакладная.Ссылка = СуммыПроводок.ДокументСсылка
Преимущества этого подхода:
При изменении запросов типовых динамических списков, особенно через расширения, всегда есть риск нарушить работу связанного функционала. В данном случае выяснилось, что после доработки перестал формироваться отчет "Реестр документов" для требований-накладных.
Это происходит потому, что некоторые отчеты могут использовать настройки и текст запроса из формы списка объекта. Для исправления может потребоваться дополнительная доработка в расширении, например, в общей форме отчета или в функциях, формирующих его запрос (в примере была упомянута функция Расш_УточнитьТекстЗапросаПоДопРеквизитам в отчете РеестрДокументов).
Всегда проводите комплексное тестирование после подобных доработок! Проверяйте не только работу самой формы, но и связанных отчетов и обработок.