В практике разработчика 1С часто возникает задача фильтрации документов не по реквизитам шапки, а по совокупному состоянию строк его табличной части. Типичный пример — поиск заказов клиентов, выполнение которых полностью прекращено (все позиции отменены). На первый взгляд задача кажется простой, но она таит в себе несколько технических нюансов, связанных с производительностью СУБД и корректностью обработки данных.
В этой статье мы подробно разберем несколько подходов к решению этой задачи, проанализируем их плюсы и минусы, а также выясним, какой метод будет наиболее эффективным для высоконагруженных систем.
Это самый изящный и короткий способ написания запроса. В платформе 1С:Предприятие булево значение Истина при преобразовании в SQL-запрос интерпретируется как 1, а Ложь — как 0. Мы можем использовать это свойство для группировки.
Рассмотрим логику: если в заказе есть хотя бы одна неотмененная строка (Отменено = Ложь), то минимальное значение поля Отменено в группе строк этого заказа будет равно 0 (Ложь). Если же абсолютно все строки имеют значение Истина, то и минимальное значение будет Истина.
Посмотрим на пример реализации такого запроса:
ВЫБРАТЬ
ЗаказТовары.Ссылка КАК Ссылка
ИЗ
Документ.ЗаказКлиента.Товары КАК ЗаказТовары
ГДЕ
ЗаказТовары.Ссылка.Проведен = ИСТИНА
СГРУППИРОВАТЬ ПО
ЗаказТовары.Ссылка
ИМЕЮЩИЕ
МИНИМУМ(ЗаказТовары.Отменено) = ИСТИНА
Проанализируем этот код. Мы обращаемся к табличной части Товары, группируем записи по полю Ссылка и накладываем условие в секции ИМЕЮЩИЕ. Этот метод отлично подходит для небольших и средних баз данных.
Другой подход заключается в математическом сравнении общего количества строк в документе и количества строк, у которых установлен признак отмены. Если эти числа равны, значит, заказ отменен полностью.
Разберем по шагам, как это реализовать в одном запросе:
ВЫБРАТЬ
ЗаказТовары.Ссылка КАК Ссылка,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЗаказТовары.НомерСтроки) КАК ВсегоСтрок,
СУММА(ВЫБОР
КОГДА ЗаказТовары.Отменено
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ) КАК КоличествоОтмененных
ИЗ
Документ.ЗаказКлиента.Товары КАК ЗаказТовары
СГРУППИРОВАТЬ ПО
ЗаказТовары.Ссылка
ИМЕЮЩИЕ
СУММА(ВЫБОР
КОГДА ЗаказТовары.Отменено
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ) = КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЗаказТовары.НомерСтроки)
Этот метод более нагляден, однако он более громоздкий в написании и требует от СУБД выполнения дополнительных вычислительных операций (суммирования и подсчета количества) для каждой строки табличной части.
С точки зрения производительности на больших объемах данных (миллионы строк в табличных частях), использование группировки GROUP BY может быть ресурсоемким. Рассмотрим альтернативный метод: выберем все уникальные ссылки заказов, но отсечем те, для которых существует хотя бы одна строка, где Отменено = Ложь.
Этот подход часто называют «методом от противного». Проанализируем пример с использованием оператора НЕ В:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ЗаказТовары.Ссылка КАК Ссылка
ИЗ
Документ.ЗаказКлиента.Товары КАК ЗаказТовары
ГДЕ
ЗаказТовары.Ссылка НЕ В
(ВЫБРАТЬ
Т.Ссылка
ИЗ
Документ.ЗаказКлиента.Товары КАК Т
ГДЕ
Т.Отменено = ЛОЖЬ)
Почему это может работать быстрее? В современных СУБД (MS SQL Server, PostgreSQL) оптимизатор запросов может эффективно обработать такие конструкции. Если в табличной части есть индекс по полю Отменено (что встречается редко, но возможно) или по Ссылка, поиск первой же строки со значением Ложь позволит СУБД сразу исключить весь документ из выборки, не сканируя остальные его строки.
При выборе любого из предложенных методов необходимо учитывать специфику работы платформы 1С и бизнес-логику системы.
Документ.ЗаказКлиента.Товары. Если в заказе вообще нет строк, он не попадет в выборку. Если по вашей логике пустой заказ также считается «отмененным», вам необходимо начинать запрос от основной таблицы документов Документ.ЗаказКлиента и использовать ЛЕВОЕ СОЕДИНЕНИЕ с таблицей товаров.Проведен) обязательно должны находиться в секции ГДЕ. Если поместить их в ИМЕЮЩИЕ, СУБД сначала сгруппирует все заказы за всю историю существования базы, и только потом отсечет лишние, что приведет к критическому замедлению системы.Статус документа или его реквизит Закрыт при отмене всех строк. Поиск по индексированному полю в шапке всегда в десятки раз быстрее, чем запрос к табличным частям.Отменено в табличных частях обычно не индексируется. Чтобы избежать Full Scan (полного сканирования таблицы), всегда ограничивайте выборку жестким периодом (Дата МЕЖДУ &ДатаНачала И &ДатаКонца).Для повседневных задач и небольших отчетов мы рекомендуем использовать Метод 1 с функцией МИНИМУМ, так как он наиболее прост в поддержке и чтении кода.
Если же вы разрабатываете критически важный узел системы, работающий с огромными массивами данных, рассмотрите Метод 3 или использование временных таблиц для предварительного отбора документов. Это позволит минимизировать нагрузку на сервер БД и обеспечить стабильную работу программы.