Как правильно использовать временную таблицу в условии запроса 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке сложных отчетов и алгоритмов обработки данных в платформе 1С:Предприятие часто возникает необходимость отобрать данные из одной таблицы на основе списка значений, уже помещенных во временную таблицу. Начинающие разработчики нередко сталкиваются с синтаксической ошибкой, пытаясь напрямую передать имя временной таблицы в оператор проверки вхождения В.

Разберем типичную ошибку в тексте запроса, выясним причину ее возникновения и рассмотрим правильные варианты реализации выборки с учетом рекомендаций стандартов разработки 1С и оптимизации производительности СУБД.

В чем заключается ошибка при обращении к временной таблице в условии

Проанализируем исходный фрагмент запроса, вызвавший затруднение:


ВЫБРАТЬ
	ПриходнаяНакладная.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ
ИЗ
	Документ.ПриходнаяНакладная КАК ПриходнаяНакладная
ГДЕ
	ПриходнаяНакладная.Ссылка В(&Ссылка)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	Контрагенты.Ссылка КАК Ссылка
ИЗ
	Справочник.Контрагенты КАК Контрагенты
ГДЕ
	Контрагенты.Ссылка В (ВТ)

При выполнении такого кода платформа выдаст синтаксическую ошибку. Причина кроется в правилах языка запросов 1С. Оператор В (...) ожидает внутри круглых скобок один из двух допустимых вариантов:

  1. Параметр запроса, содержащий массив, список значений или массив ссылок (например, ГДЕ Поле В (&МассивЗначений)).
  2. Полноценный вложенный подзапрос, возвращающий ровно одну колонку.

Передавать имя временной таблицы напрямую в скобки нельзя, так как имя таблицы само по себе не является выражением выборки. Платформа не может автоматически определить, по какому именно полю из структуры временной таблицы должно выполняться сопоставление.

Способ 1. Использование подзапроса в операторе В

Для исправления синтаксической ошибки достаточно явно оформить выборку колонки из временной таблицы с помощью вложенного запроса. Посмотрим на рабочий вариант:


ВЫБРАТЬ
	Контрагенты.Ссылка КАК Ссылка
ИЗ
	Справочник.Контрагенты КАК Контрагенты
ГДЕ
	Контрагенты.Ссылка В
		(ВЫБРАТЬ
			ВТ.Контрагент КАК Контрагент
		ИЗ
			ВТ КАК ВТ)

Рассмотрим важные правила применения подзапроса внутри оператора В:

  1. Строго одна колонка в выборке. Вложенный запрос (ВЫБРАТЬ ... ИЗ ВТ) должен содержать только одно поле. Если выбрать две или более колонок, запрос завершится аварийно.
  2. Отсутствие поддержки составных условий (кортежей). В языке запросов 1С нельзя написать конструкцию вида ГДЕ (Поле1, Поле2) В (ВЫБРАТЬ ...). Если требуется фильтрация сразу по комбинации нескольких полей (например, по номенклатуре и характеристике), оператор В не подойдет в принципе.
  3. Проблема значений NULL. Если в колонке временной таблицы окажется значение NULL, реляционная СУБД из-за особенностей трехзначной логики SQL может обработать предикат IN не так, как ожидает разработчик (результат сравнения с NULL дает UNKNOWN). Рекомендуется заранее исключать пустые значения при формировании временной таблицы с помощью ГДЕ Поле ЕСТЬ НЕ NULL.

Способ 2. Внутреннее соединение (рекомендуемый стандарт разработки)

Хотя вариант с подзапросом синтаксически корректен, стандарты разработки 1С рекомендуют заменять конструкцию ГДЕ Поле В (ВЫБРАТЬ ...) на ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN). На уровне СУБД предикат IN или EXISTS с подзапросом нередко трансформируется в неоптимальные планы выполнения (например, вложенные циклы Nested Loops со сканированием всей таблицы). Внутреннее соединение дает оптимизатору СУБД гораздо больше гибкости для выбора эффективного плана выполнения (Hash Match, Merge Join).

Перепишем наш пакет запросов в соответствии с лучшими практиками:


ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПриходнаяНакладная.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_Контрагенты
ИЗ
	Документ.ПриходнаяНакладная КАК ПриходнаяНакладная
ГДЕ
	ПриходнаяНакладная.Ссылка В(&Ссылка)
	И ПриходнаяНакладная.Контрагент ЕСТЬ НЕ NULL

ИНДЕКСИРОВАТЬ ПО
	Контрагент
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	Контрагенты.Ссылка КАК Ссылка
ИЗ
	Справочник.Контрагенты КАК Контрагенты
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Контрагенты КАК ВТ_Контрагенты
		ПО Контрагенты.Ссылка = ВТ_Контрагенты.Контрагент

Нюансы производительности и частые подводные камни

Разберем ключевые технические моменты, на которые необходимо обращать внимание при работе с временными таблицами:

  1. Задвоение записей при соединении. Оператор В (ВЫБРАТЬ ...) проверяет только факт наличия значения в списке, поэтому дубли в подзапросе не приводят к размножению строк итогового результата. Однако при переходе на ВНУТРЕННЕЕ СОЕДИНЕНИЕ каждая строка основной таблицы соединится со всеми совпавшими строками временной таблицы. Если во временной таблице контрагент встречается несколько раз, в результирующей выборке появятся дубли. Чтобы предотвратить это, всегда используем ключевое слово РАЗЛИЧНЫЕ или группировку при заполнении временной таблицы.
  2. Индексация временных таблиц. Если временная таблица содержит более нескольких десятков записей, поле связи обязательно следует индексировать с помощью инструкции ИНДЕКСИРОВАТЬ ПО. Без явного индекса СУБД будет вынуждена сканировать временную таблицу целиком при каждой операции сопоставления.
  3. Составные условия. В отличие от оператора В, внутреннее соединение позволяет легко связывать таблицы сразу по нескольким колонкам через логическое И в секции ПО, что делает этот подход универсальным.
← На главную