При разработке сложных отчетов и обработок в платформе 1С часто возникает задача собрать цепочку связанных документов: заказы, перемещения, поступления. Для этого несколько выборок объединяются с помощью конструкции ОБЪЕДИНИТЬ ВСЕ, формируя единое составное поле (например, Документ). Сложности начинаются тогда, когда требуется наложить ограничение по периоду не на весь результат, а только на один конкретный вид документа — например, отобрать только поступления за указанные даты, сохранив при этом все остальные документы цепочки без ограничений по дате. Рассмотрим, почему типовые попытки решить эту задачу приводят к зависанию запроса или некорректным результатам, и разберем правильные варианты реализации.
Разберем типичную ситуацию: разработчик объединяет табличные части документов ВнутреннийЗаказ, ЗаказПоставщику, ПеремещениеТоваров и ПоступлениеТоваровУслуг, помещая результат во временную таблицу ВТ. Далее во второй временной таблице делается попытка отфильтровать поступления с помощью конструкции ВЫБОР КОГДА:
ГДЕ
ВЫБОР
КОГДА ВТ.Документ ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА ВЫРАЗИТЬ(ВТ.Документ КАК Документ.ПоступлениеТоваровУслуг).Дата МЕЖДУ &Нач И &Кон
ИНАЧЕ ИСТИНА
КОНЕЦ
На практике такой подход приводит к двум серьезным проблемам:
Документ во временной таблице имеет составной тип. Обращение через точку к реквизиту Дата заставляет платформу генерировать неявные левые соединения (LEFT JOIN) с таблицами всех типов документов, входящих в составной тип. Поскольку временная таблица не была проиндексирована по полю Документ, СУБД выполняет полное сканирование (Table Scan) в системной базе tempdb. В сочетании со сложным условным оператором в секции ГДЕ это полностью исключает использование индексов по полю Дата.ВЫРАЗИТЬ(...).Дата МЕЖДУ &Нач И &Кон непосредственно в списке полей ВЫБРАТЬ в старых версиях платформы порождает синтаксическую ошибку или неверный SQL-код, так как в большинстве СУБД (включая MS SQL Server) отсутствует полноценный булев тип для столбцов выборки.ОБЪЕДИНИТЬ ВСЕ собирает независимые строки. Если запрос отобрал сам внутренний заказ, перемещения и заказы поставщикам, а фильтр отсек только строки поступлений, в итоговом результате внутренний заказ все равно останется. Разработчику может ошибочно показаться, что фильтр по дате не сработал вовсе, хотя на самом деле отсеялись только строки поступлений.Самый правильный и производительный подход с точки зрения архитектуры СУБД — отсекать лишние данные как можно раньше, еще до их попадания во временную таблицу или объединение. Если условие по дате относится исключительно к документу ПоступлениеТоваровУслуг, его необходимо прописать непосредственно в секции ГДЕ той ветки ОБЪЕДИНИТЬ ВСЕ, которая выбирает поступления.
Посмотрим, как выглядит оптимизированный запрос первого этапа:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ВнутреннийЗаказТовары.Ссылка КАК ВнутреннийЗаказ,
ВнутреннийЗаказТовары.Ссылка КАК Документ
ПОМЕСТИТЬ ВТ
ИЗ
Документ.ВнутреннийЗаказ.Товары КАК ВнутреннийЗаказТовары
ГДЕ
ВнутреннийЗаказТовары.Ссылка.ПометкаУдаления = ЛОЖЬ
И ВЫБОР
КОГДА ВнутреннийЗаказТовары.Ссылка.Заказчик ССЫЛКА Справочник.Склады
ТОГДА ВнутреннийЗаказТовары.Ссылка.Заказчик В (&СписокПеремСклад)
ИНАЧЕ ВнутреннийЗаказТовары.Ссылка.Заказчик В (&СписокПеремПодразделение)
КОНЕЦ
СГРУППИРОВАТЬ ПО
ВнутреннийЗаказТовары.Ссылка
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ЗаказПоставщикуТовары.Заказ,
ЗаказПоставщикуТовары.Ссылка
ИЗ
Документ.ЗаказПоставщику.Товары КАК ЗаказПоставщикуТовары
ГДЕ
ЗаказПоставщикуТовары.Ссылка.ПометкаУдаления = ЛОЖЬ
И ЗаказПоставщикуТовары.Заказ ССЫЛКА Документ.ВнутреннийЗаказ
СГРУППИРОВАТЬ ПО
ЗаказПоставщикуТовары.Ссылка,
ЗаказПоставщикуТовары.Заказ
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ПеремещениеТоваровТовары.ВнутреннийЗаказ,
ПеремещениеТоваровТовары.Ссылка
ИЗ
Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
ГДЕ
ПеремещениеТоваровТовары.Ссылка.ПометкаУдаления = ЛОЖЬ
И ПеремещениеТоваровТовары.ВнутреннийЗаказ ССЫЛКА Документ.ВнутреннийЗаказ
СГРУППИРОВАТЬ ПО
ПеремещениеТоваровТовары.Ссылка,
ПеремещениеТоваровТовары.ВнутреннийЗаказ
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ПоступлениеТоваровУслугТовары.Заказ,
ПоступлениеТоваровУслугТовары.Ссылка
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ГДЕ
ПоступлениеТоваровУслугТовары.Ссылка.ПометкаУдаления = ЛОЖЬ
И ПоступлениеТоваровУслугТовары.Заказ ССЫЛКА Документ.ВнутреннийЗаказ
И ПоступлениеТоваровУслугТовары.Ссылка.Дата МЕЖДУ &Нач И &Кон
СГРУППИРОВАТЬ ПО
ПоступлениеТоваровУслугТовары.Заказ,
ПоступлениеТоваровУслугТовары.Ссылка
;
Обратим внимание на ключевые преимущества такого подхода:
ВТ сразу формируется с отфильтрованными данными. В нее не записываются лишние поступления товаров, что снижает нагрузку на дисковую подсистему сервера 1С и СУБД.ПоступлениеТоваровУслугТовары.Ссылка.Дата СУБД использует существующий кластерный индекс таблицы документа, что обеспечивает мгновенную фильтрацию.РезультатВТ — данные можно сразу передавать в финальную выборку отчета.Если логика алгоритма строго требует сначала собрать все документы без исключения во временную таблицу, а фильтрацию наложить позже, разберем, как сделать это без деградации производительности.
Во-первых, временную таблицу необходимо обязательно проиндексировать по составному полю. Во-вторых, вместо громоздкой конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ИСТИНА КОНЕЦ в секции ГДЕ следует использовать стандартную булеву логику (дизъюнкцию условий):
ВЫБРАТЬ
ВТ.ВнутреннийЗаказ КАК ВнутреннийЗаказ,
ВТ.Документ КАК Документ
ПОМЕСТИТЬ ВТ
ИНДЕКСИРОВАТЬ ПО
Документ
ИЗ
... // Блок объединения документов
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.ВнутреннийЗаказ КАК ВнутреннийЗаказ,
ВТ.Документ КАК Документ
ИЗ
ВТ КАК ВТ
ГДЕ
НЕ ВТ.Документ ССЫЛКА Документ.ПоступлениеТоваровУслуг
ИЛИ (ВЫРАЗИТЬ(ВТ.Документ КАК Документ.ПоступлениеТоваровУслуг).Дата МЕЖДУ &Нач И &Кон)
Такое условие читается СУБД гораздо эффективнее: если документ не является поступлением, вторая часть выражения даже не вычисляется.
Проанализируем две распространенные ошибки, из-за которых кажется, что отбор по датам в 1С «не работает»:
Дата всегда содержит время с точностью до секунды. Если передать в параметр &Кон дату вида 31.01.2025 00:00:00, то документ поступления, проведенный 31.01.2025 14:30:00, не попадет в условие МЕЖДУ. Конечную дату необходимо обязательно приводить к концу дня с помощью функции КонецДня(&Кон) во встроенном языке или использовать функцию языка запросов КОНЕЦПЕРИОДА(&Кон, ДЕНЬ).ИТОГИ ПО), использование дополнительной секции СГРУППИРОВАТЬ ПО без агрегатных функций (СУММА, КОЛИЧЕСТВО) является избыточным и создает лишнюю нагрузку на оптимизатор запросов СУБД. Ее следует исключить.