Как правильно использовать операторы В и НЕ В с временными таблицами в запросах 1С?

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

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

Причина ошибки при работе с временной таблицей

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


ГДЕ
    ЧекККМ.Ссылка В(&СписокЧеков)

В данном выражении допущена ошибка: &СписокЧеков с символом амперсанда & обозначает параметр запроса, передаваемый из встроенного языка через метод УстановитьПараметр() (например, массив ссылок или список значений). Поскольку СписокЧеков в пакете запросов является временной таблицей, обращаться к ней напрямую как к параметру нельзя.

Вариант 1. Использование подзапроса к временной таблице

Если данные уже находятся во временной таблице внутри текущего пакета, для проверки вхождения значения необходимо выполнить выборку поля из этой таблицы через подзапрос.

Разберем, как выглядит корректный синтаксис для условия вхождения:


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

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

  1. Перед проверяемым полем: НЕ ЧекККМ.Ссылка В (ВЫБРАТЬ СписокЧеков.Ссылка ИЗ СписокЧеков КАК СписокЧеков)
  2. Непосредственно перед оператором: ЧекККМ.Ссылка НЕ В (ВЫБРАТЬ СписокЧеков.Ссылка ИЗ СписокЧеков КАК СписокЧеков)

Вариант 2. Передача списка через параметр запроса

Если отбор выполняется не по временной таблице, а по готовому списку значений или массиву ссылок, сформированному во встроенном языке 1С, синтаксис с амперсандом будет абсолютно верным:


// Во встроенном языке 1С:
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ЧекККМ.Ссылка КАК Ссылка
    |ИЗ
    |   Документ.ЧекККМ КАК ЧекККМ
    |ГДЕ
    |   НЕ ЧекККМ.Ссылка В (&СписокЧеков)";

Запрос.УстановитьПараметр("СписокЧеков", МассивСсылок);

Вариант 3. Оптимальный способ исключения данных через ЛЕВОЕ СОЕДИНЕНИЕ

Рассмотрим важную особенность производительности. Использование конструкции НЕ ... В (ВЫБРАТЬ ...) транслируется СУБД в оператор NOT IN. На больших объемах данных это часто приводит к существенному замедлению выполнения запроса (сканирование таблиц, вложенные циклы). Кроме того, если в подзапросе окажется значение NULL, условие NOT IN в реляционных базах вернет неопределенный результат, и итоговая выборка может оказаться пустой.

По стандартам разработки 1С оператор НЕ В при работе с таблицами рекомендуется заменять на левое соединение с проверкой на NULL (Anti-Join). Посмотрим на реализацию полного оптимизированного запроса:


ВЫБРАТЬ
    ЧекККМ.ЧекККМПродажа КАК Ссылка
ПОМЕСТИТЬ СписокЧеков
ИЗ
    Документ.ЧекККМ КАК ЧекККМ
ГДЕ
    ЧекККМ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
    И ЧекККМ.ЧекККМПродажа <> ЗНАЧЕНИЕ(Документ.ЧекККМ.ПустаяСсылка)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ЧекККМ.Ссылка
ИЗ
    Документ.ЧекККМ КАК ЧекККМ
ГДЕ
    ЧекККМ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
    И ЧекККМ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийЧекККМ.Возврат)

ИНДЕКСИРОВАТЬ ПО
    Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЧекККМ.Ссылка КАК Ссылка
ИЗ
    Документ.ЧекККМ КАК ЧекККМ
    ЛЕВОЕ СОЕДИНЕНИЕ СписокЧеков КАК СписокЧеков
        ПО ЧекККМ.Ссылка = СписокЧеков.Ссылка
ГДЕ
    СписокЧеков.Ссылка ЕСТЬ NULL

Дополнительные рекомендации по качеству кода

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