При разработке отчетов и обработок в 1С часто возникает задача: выбрать документы, ссылочный реквизит которых удовлетворяет определенному условию из связанного справочника — например, элемент не должен быть помечен на удаление или должен входить в определенный список. Начинающие разработчики нередко разбивают такую операцию на несколько отдельных обращений к базе данных или испытывают сложности с передачей результата одной выборки в условие другой. Разберем по шагам несколько эффективных способов решения этой задачи в рамках одного пакетного запроса, а также проанализируем нюансы производительности каждого подхода.
Рассмотрим вариант, при котором мы сначала отбираем подходящие элементы справочника в отдельную временную таблицу, а затем проверяем вхождение реквизита документа в эту выборку. Этот подход удобен, когда логика отбора элементов справочника сложная или в дальнейшем этот список потребуется использовать в нескольких местах запроса.
Посмотрим на пример пакетного запроса:
ВЫБРАТЬ
Кос_ТипыПоступления.Ссылка КАК Ссылка
ПОМЕСТИТЬ СписокТипов
ИЗ
Справочник.Кос_ТипыПоступления КАК Кос_ТипыПоступления
ГДЕ
НЕ Кос_ТипыПоступления.ПометкаУдаления
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ПоступлениеТоваровУслуг.Ссылка КАК Ссылка,
ПоступлениеТоваровУслуг.Дата КАК Дата,
ПоступлениеТоваровУслуг.Кос_ТипПоступления КАК Кос_ТипПоступления
ИЗ
Документ.ПоступлениеТоваровУслуг КАК ПоступлениеТоваровУслуг
ГДЕ
ПоступлениеТоваровУслуг.Дата МЕЖДУ &ДатаНач И &ДатаКон
И ПоступлениеТоваровУслуг.Кос_ТипПоступления В
(ВЫБРАТЬ
СписокТипов.Ссылка
ИЗ
СписокТипов КАК СписокТипов)
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ СписокТипов
Проанализируем ключевые моменты этой конструкции:
ИНДЕКСИРОВАТЬ ПО Ссылка. При росте объема данных в справочнике отсутствие индекса заставит СУБД выполнять полное сканирование временной таблицы для каждой проверяемой строки документа. Наличие индекса гарантирует быстрый поиск по временной таблице.УНИЧТОЖИТЬ для освобождения памяти и табличного пространства СУБД сразу после выполнения запроса.В (ВЫБРАТЬ ...) работает стабильно и предсказуемо именно тогда, когда во вложенной выборке используется предварительно подготовленная временная таблица, а не реальная таблица базы данных.Если задача заключается только в фильтрации документов по действующим элементам справочника, наиболее лаконичным и производительным классическим решением является соединение таблиц. Рассмотрим, как составить такой запрос без создания промежуточных временных таблиц:
ВЫБРАТЬ
ПоступлениеТоваровУслуг.Ссылка КАК Ссылка,
ПоступлениеТоваровУслуг.Дата КАК Дата,
ПоступлениеТоваровУслуг.Кос_ТипПоступления КАК Кос_ТипПоступления
ИЗ
Документ.ПоступлениеТоваровУслуг КАК ПоступлениеТоваровУслуг
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Кос_ТипыПоступления КАК Кос_ТипыПоступления
ПО ПоступлениеТоваровУслуг.Кос_ТипПоступления = Кос_ТипыПоступления.Ссылка
ГДЕ
ПоступлениеТоваровУслуг.Дата МЕЖДУ &ДатаНач И &ДатаКон
И НЕ Кос_ТипыПоступления.ПометкаУдаления
Выясним причину эффективности этого метода:
Кос_ТипПоступления не заполнен, содержит значение NULL или ссылается на отсутствующую запись.НЕ Кос_ТипыПоступления.ПометкаУдаления в секции ГДЕ дополнительно отсекает документы с удаленными типами поступления. Оптимизатор СУБД объединяет эти условия в оптимальный план выполнения запроса.Самый простой способ отбора, который часто используют разработчики, — прямое обращение к реквизиту связанной таблицы через точку в условии ГДЕ:
ВЫБРАТЬ
ПоступлениеТоваровУслуг.Ссылка КАК Ссылка,
ПоступлениеТоваровУслуг.Дата КАК Дата,
ПоступлениеТоваровУслуг.Кос_ТипПоступления КАК Кос_ТипПоступления
ИЗ
Документ.ПоступлениеТоваровУслуг КАК ПоступлениеТоваровУслуг
ГДЕ
ПоступлениеТоваровУслуг.Дата МЕЖДУ &ДатаНач И &ДатаКон
И НЕ ПоступлениеТоваровУслуг.Кос_ТипПоступления.ПометкаУдаления
Разберем подробнее, как этот код воспринимается платформой и СУБД:
Таблица.Реквизит.Свойство транслируется платформой 1С в СУБД как LEFT OUTER JOIN. Однако проверка условия на равенство или отрицание булевого поля автоматически отсекает строки, где связанное поле равно NULL, фактически сводя соединение к внутреннему.ВЫРАЗИТЬ:
ГДЕ НЕ ВЫРАЗИТЬ(ПоступлениеТоваровУслуг.Кос_ТипПоступления КАК Справочник.Кос_ТипыПоступления).ПометкаУдаления
В процессе модификации условий часто требуется проверить, заполнен ли реквизит документа, без передачи пустой ссылки через отдельный параметр. Для этого в языке запросов 1С предусмотрена специальная литеральная конструкция ЗНАЧЕНИЕ.
Посмотрим на пример проверки реквизита на пустоту:
ГДЕ
ПоступлениеТоваровУслуг.Кос_ТипПоступления <> ЗНАЧЕНИЕ(Справочник.Кос_ТипыПоступления.ПустаяСсылка)
Важно помнить: выражение ЗНАЧЕНИЕ(...) возвращает пустую ссылку конкретного типа метаданных. Если поле в результате левого соединения содержит NULL, условие проверки на пустую ссылку вернет ЛОЖЬ. Для комплексной проверки на заполненность используем совместное условие:
ГДЕ
ПоступлениеТоваровУслуг.Кос_ТипПоступления ЕСТЬ НЕ NULL
И ПоступлениеТоваровУслуг.Кос_ТипПоступления <> ЗНАЧЕНИЕ(Справочник.Кос_ТипыПоступления.ПустаяСсылка)
При отборе документов по реквизиту Дата всегда учитываем, что поле содержит не только дату, но и время. Конструкция ПоступлениеТоваровУслуг.Дата МЕЖДУ &ДатаНач И &ДатаКон включает обе граничные точки.
Если в параметр &ДатаКон передается дата без указания времени (например, 01.05.2024 00:00:00), то документы, проведенные в течение дня 1 мая (например, в 14:30), не попадут в выборку. Чтобы избежать ошибок, в коде модуля перед установкой параметра обязательно приводим дату к концу периода с помощью функции КонецДня(&ДатаКон) либо задаем условие строгого полуинтервала:
ГДЕ
ПоступлениеТоваровУслуг.Дата >= &ДатаНач
И ПоступлениеТоваровУслуг.Дата < &ДатаКонВходящая
Поле Дата у документов входит в кластерный индекс таблицы, поэтому корректно заданное условие по периоду позволяет СУБД быстро сократить объем сканируемых строк перед выполнением любых соединений и условий.