При разработке сложных отчетов и алгоритмов обработки данных в платформе 1С:Предприятие часто возникает необходимость отобрать данные из одной таблицы на основе списка значений, уже помещенных во временную таблицу. Начинающие разработчики нередко сталкиваются с синтаксической ошибкой, пытаясь напрямую передать имя временной таблицы в оператор проверки вхождения В.
Разберем типичную ошибку в тексте запроса, выясним причину ее возникновения и рассмотрим правильные варианты реализации выборки с учетом рекомендаций стандартов разработки 1С и оптимизации производительности СУБД.
Проанализируем исходный фрагмент запроса, вызвавший затруднение:
ВЫБРАТЬ
ПриходнаяНакладная.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ
ИЗ
Документ.ПриходнаяНакладная КАК ПриходнаяНакладная
ГДЕ
ПриходнаяНакладная.Ссылка В(&Ссылка)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Контрагенты.Ссылка КАК Ссылка
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
Контрагенты.Ссылка В (ВТ)
При выполнении такого кода платформа выдаст синтаксическую ошибку. Причина кроется в правилах языка запросов 1С. Оператор В (...) ожидает внутри круглых скобок один из двух допустимых вариантов:
ГДЕ Поле В (&МассивЗначений)).Передавать имя временной таблицы напрямую в скобки нельзя, так как имя таблицы само по себе не является выражением выборки. Платформа не может автоматически определить, по какому именно полю из структуры временной таблицы должно выполняться сопоставление.
Для исправления синтаксической ошибки достаточно явно оформить выборку колонки из временной таблицы с помощью вложенного запроса. Посмотрим на рабочий вариант:
ВЫБРАТЬ
Контрагенты.Ссылка КАК Ссылка
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
Контрагенты.Ссылка В
(ВЫБРАТЬ
ВТ.Контрагент КАК Контрагент
ИЗ
ВТ КАК ВТ)
Рассмотрим важные правила применения подзапроса внутри оператора В:
(ВЫБРАТЬ ... ИЗ ВТ) должен содержать только одно поле. Если выбрать две или более колонок, запрос завершится аварийно.ГДЕ (Поле1, Поле2) В (ВЫБРАТЬ ...). Если требуется фильтрация сразу по комбинации нескольких полей (например, по номенклатуре и характеристике), оператор В не подойдет в принципе.NULL, реляционная СУБД из-за особенностей трехзначной логики SQL может обработать предикат IN не так, как ожидает разработчик (результат сравнения с NULL дает UNKNOWN). Рекомендуется заранее исключать пустые значения при формировании временной таблицы с помощью ГДЕ Поле ЕСТЬ НЕ NULL.Хотя вариант с подзапросом синтаксически корректен, стандарты разработки 1С рекомендуют заменять конструкцию ГДЕ Поле В (ВЫБРАТЬ ...) на ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN). На уровне СУБД предикат IN или EXISTS с подзапросом нередко трансформируется в неоптимальные планы выполнения (например, вложенные циклы Nested Loops со сканированием всей таблицы). Внутреннее соединение дает оптимизатору СУБД гораздо больше гибкости для выбора эффективного плана выполнения (Hash Match, Merge Join).
Перепишем наш пакет запросов в соответствии с лучшими практиками:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ПриходнаяНакладная.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_Контрагенты
ИЗ
Документ.ПриходнаяНакладная КАК ПриходнаяНакладная
ГДЕ
ПриходнаяНакладная.Ссылка В(&Ссылка)
И ПриходнаяНакладная.Контрагент ЕСТЬ НЕ NULL
ИНДЕКСИРОВАТЬ ПО
Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Контрагенты.Ссылка КАК Ссылка
ИЗ
Справочник.Контрагенты КАК Контрагенты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Контрагенты КАК ВТ_Контрагенты
ПО Контрагенты.Ссылка = ВТ_Контрагенты.Контрагент
Разберем ключевые технические моменты, на которые необходимо обращать внимание при работе с временными таблицами:
В (ВЫБРАТЬ ...) проверяет только факт наличия значения в списке, поэтому дубли в подзапросе не приводят к размножению строк итогового результата. Однако при переходе на ВНУТРЕННЕЕ СОЕДИНЕНИЕ каждая строка основной таблицы соединится со всеми совпавшими строками временной таблицы. Если во временной таблице контрагент встречается несколько раз, в результирующей выборке появятся дубли. Чтобы предотвратить это, всегда используем ключевое слово РАЗЛИЧНЫЕ или группировку при заполнении временной таблицы.ИНДЕКСИРОВАТЬ ПО. Без явного индекса СУБД будет вынуждена сканировать временную таблицу целиком при каждой операции сопоставления.В, внутреннее соединение позволяет легко связывать таблицы сразу по нескольким колонкам через логическое И в секции ПО, что делает этот подход универсальным.