Работа с запросами в системе 1С:Предприятие 8 требует не только понимания синтаксиса, но и знания механизмов работы платформы с СУБД. Одной из частых задач является использование данных из временной таблицы для фильтрации основного набора записей. Однако на этом пути начинающие разработчики часто сталкиваются с логическими ошибками, которые приводят либо к пустому результату, либо к существенному замедлению работы программы. Рассмотрим подробно, как правильно указать временную таблицу в условии и какие ошибки чаще всего встречаются в коде.
Проанализируем фрагмент кода, где разработчик пытается выбрать контактных лиц, чьи родители не входят в предварительно сформированную временную таблицу Группы. Основная проблема заключалась в некорректном использовании ЛЕВОГО СОЕДИНЕНИЯ и противоречивых условиях в блоке ГДЕ.
Первая критическая ошибка заключалась в условии соединения: ПО Группы.Ссылка = КонтактныеЛица.Ссылка. Если таблица Группы содержит только родительские элементы, а в основной выборке мы ищем подчиненные элементы, то такое равенство ссылок практически никогда не даст результата, так как объект не может быть одновременно и «родителем для самого себя», и «дочерним элементом», который мы ищем по фильтру.
Вторая ошибка — использование временной таблицы Группы как основной (первой в списке ИЗ), в то время как условие в ГДЕ пытается исключить данные на основе этой же таблицы. Если мы выбираем данные ИЗ таблицы А и при этом говорим «где данные НЕ В таблице А», мы гарантированно получим пустой результат.
В 1С существует понятие «обращение через точку» (разыменование ссылки). В исходном запросе часто встречалось написание КонтактныеЛица.Ссылка.Родитель. Проанализируем, почему это плохо. Поле Ссылка уже содержит уникальный идентификатор записи, а Родитель — это другое поле той же самой таблицы справочника.
Когда вы пишете КонтактныеЛица.Ссылка.Родитель, платформа вынуждена делать неявное соединение таблицы справочника самой с собой, чтобы получить значение поля. Это создает лишнюю нагрузку на SQL-сервер. Правильно обращаться напрямую к полю: КонтактныеЛица.Родитель. Это правило касается любых стандартных и дополнительных реквизитов: КонтактныеЛица.Наименование вместо КонтактныеЛица.Ссылка.Наименование.
Для того чтобы указать временную таблицу в условии, мы можем использовать подзапрос к ней. Рассмотрим, как это выглядит в исправленном виде:
ВЫБРАТЬ
КонтактныеЛица.Ссылка КАК Ссылка,
КонтактныеЛица.Родитель КАК Родитель
ИЗ
Справочник.КонтактныеЛица КАК КонтактныеЛица
ГДЕ
НЕ КонтактныеЛица.Родитель В (ВЫБРАТЬ Группы.Ссылка ИЗ Группы)
И НЕ КонтактныеЛица.ЭтоГруппа
В данном примере мы выбираем всех контактных лиц, чей родитель отсутствует во временной таблице Группы. Обратите внимание, что внутри скобок после оператора В мы обязательно должны написать полноценный микро-запрос ВЫБРАТЬ ... ИЗ ....
Хотя использование оператора НЕ В интуитивно понятно, с точки зрения производительности (особенно на больших объемах данных) эффективнее использовать ЛЕВОЕ СОЕДИНЕНИE с последующей проверкой на NULL. Разберем этот метод по шагам:
ГДЕ указываем, что поле из присоединенной таблицы должно быть равно NULL.Посмотрим на пример кода:
ВЫБРАТЬ
КонтактныеЛица.Ссылка КАК Ссылка
ИЗ
Справочник.КонтактныеЛица КАК КонтактныеЛица
ЛЕВОЕ СОЕДИНЕНИЕ Группы КАК Группы
ПО КонтактныеЛица.Родитель = Группы.Ссылка
ГДЕ
Группы.Ссылка ЕСТЬ NULL
И НЕ КонтактныеЛица.ЭтоГруппа
Такой подход позволяет оптимизатору СУБД более эффективно построить план выполнения запроса, используя индексы.
Если вы планируете использовать временную таблицу в соединениях или условиях В, критически важно проиндексировать её по полям связи. Без индекса СУБД будет вынуждена каждый раз сканировать всю временную таблицу целиком, что приведет к деградации производительности.
При создании временной таблицы добавьте ключевое слово ИНДЕКСИРОВАТЬ ПО:
ВЫБРАТЬ РАЗЛИЧНЫЕ
КонтактныеЛица.Родитель КАК Ссылка
ПОМЕСТИТЬ Группы
ИЗ
Справочник.КонтактныеЛица КАК КонтактныеЛица
ГДЕ
КонтактныеЛица.Ссылка В ИЕРАРХИИ(&СсылкаГруппы)
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
Важный нюанс: Индексировать стоит только те поля, по которым реально происходит поиск или соединение. Избыточное количество индексов также может замедлить работу из-за затрат на их создание.
В исходном запросе использовалось условие В ИЕРАРХИИ(&СсылкаГруппы). Нужно помнить, что это одно из самых «тяжелых» условий в 1С. Платформа преобразует его в сложный SQL-запрос, который ищет все вложенные элементы на всех уровнях. Если структура справочника очень глубокая, а записей сотни тысяч, старайтесь минимизировать количество таких вызовов. Помещение результатов иерархического поиска во временную таблицу (как и пытался сделать автор темы) — это верная стратегия, так как она позволяет выполнить тяжелый поиск один раз, а затем использовать готовый плоский список.
Проанализируем еще один момент из обсуждения — проверку типов событий. Условие ТипСобытия <> ЗНАЧЕНИЕ(...) И ТипСобытия <> ЗНАЧЕНИЕ(...) работает корректно только тогда, когда ТипСобытия не является NULL. Если вы используете ЛЕВОЕ СОЕДИНЕНИЕ с таблицей событий, и событие для участника не найдено, проверка на неравенство конкретному значению вернет ЛОЖЬ (так как NULL не равен и не «не равен» значению в классическом понимании логики SQL в данном контексте).
Для корректной фильтрации в таких случаях следует либо переносить условия в блок ПО соединения, либо явно обрабатывать NULL через функцию ЕСТЬNULL(ТипСобытия, ЗНАЧЕНИЕ(...)).
ИНДЕКСИРОВАТЬ ПО.Ссылка.ЛЕВОЕ СОЕДИНЕНИЕ и проверку на NULL.Соблюдение этих правил позволит вам писать чистый, понятный и, главное, производительный код, который будет стабильно работать даже при значительном росте объема данных в информационной базе.