При написании пакетных запросов в 1С часто возникает необходимость отобрать или, наоборот, исключить документы и справочники, список которых предварительно сформирован во временной таблице. Если допустить синтаксическую неточность при обращении к временной таблице через оператор В, платформа выдаст ошибку, либо запрос отработает некорректно. Разберем типичные ошибки, синтаксические конструкции и лучшие практики оптимизации таких условий.
Проанализируем исходную ситуацию. В пакете запросов была создана временная таблица СписокЧеков, после чего в итоговой выборке разработчик попытался применить условие:
ГДЕ
ЧекККМ.Ссылка В(&СписокЧеков)
В данном выражении допущена ошибка: &СписокЧеков с символом амперсанда & обозначает параметр запроса, передаваемый из встроенного языка через метод УстановитьПараметр() (например, массив ссылок или список значений). Поскольку СписокЧеков в пакете запросов является временной таблицей, обращаться к ней напрямую как к параметру нельзя.
Если данные уже находятся во временной таблице внутри текущего пакета, для проверки вхождения значения необходимо выполнить выборку поля из этой таблицы через подзапрос.
Разберем, как выглядит корректный синтаксис для условия вхождения:
ВЫБРАТЬ
ЧекККМ.Ссылка КАК Ссылка
ИЗ
Документ.ЧекККМ КАК ЧекККМ
ГДЕ
ЧекККМ.Ссылка В (ВЫБРАТЬ СписокЧеков.Ссылка ИЗ СписокЧеков КАК СписокЧеков)
Если же стоит задача отобрать только те чеки, которые не входят в сформированный ранее список, можно использовать отрицание. В языке запросов 1С допустимы оба варианта размещения ключевого слова НЕ:
НЕ ЧекККМ.Ссылка В (ВЫБРАТЬ СписокЧеков.Ссылка ИЗ СписокЧеков КАК СписокЧеков)ЧекККМ.Ссылка НЕ В (ВЫБРАТЬ СписокЧеков.Ссылка ИЗ СписокЧеков КАК СписокЧеков)Если отбор выполняется не по временной таблице, а по готовому списку значений или массиву ссылок, сформированному во встроенном языке 1С, синтаксис с амперсандом будет абсолютно верным:
// Во встроенном языке 1С:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЧекККМ.Ссылка КАК Ссылка
|ИЗ
| Документ.ЧекККМ КАК ЧекККМ
|ГДЕ
| НЕ ЧекККМ.Ссылка В (&СписокЧеков)";
Запрос.УстановитьПараметр("СписокЧеков", МассивСсылок);
Рассмотрим важную особенность производительности. Использование конструкции НЕ ... В (ВЫБРАТЬ ...) транслируется СУБД в оператор NOT IN. На больших объемах данных это часто приводит к существенному замедлению выполнения запроса (сканирование таблиц, вложенные циклы). Кроме того, если в подзапросе окажется значение NULL, условие NOT IN в реляционных базах вернет неопределенный результат, и итоговая выборка может оказаться пустой.
По стандартам разработки 1С оператор НЕ В при работе с таблицами рекомендуется заменять на левое соединение с проверкой на NULL (Anti-Join). Посмотрим на реализацию полного оптимизированного запроса:
ВЫБРАТЬ
ЧекККМ.ЧекККМПродажа КАК Ссылка
ПОМЕСТИТЬ СписокЧеков
ИЗ
Документ.ЧекККМ КАК ЧекККМ
ГДЕ
ЧекККМ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И ЧекККМ.ЧекККМПродажа <> ЗНАЧЕНИЕ(Документ.ЧекККМ.ПустаяСсылка)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ЧекККМ.Ссылка
ИЗ
Документ.ЧекККМ КАК ЧекККМ
ГДЕ
ЧекККМ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И ЧекККМ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийЧекККМ.Возврат)
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ЧекККМ.Ссылка КАК Ссылка
ИЗ
Документ.ЧекККМ КАК ЧекККМ
ЛЕВОЕ СОЕДИНЕНИЕ СписокЧеков КАК СписокЧеков
ПО ЧекККМ.Ссылка = СписокЧеков.Ссылка
ГДЕ
СписокЧеков.Ссылка ЕСТЬ NULL
ИНДЕКСИРОВАТЬ ПО для полей временной таблицы, по которым в дальнейшем выполняются соединения или поиск.ЧекККМ.ЧекККМПродажа.Ссылка = ЗНАЧЕНИЕ(...) обращается через точку к ссылке, создавая лишнее скрытое соединение с таблицей. Проверяем реквизит напрямую: ЧекККМ.ЧекККМПродажа <> ЗНАЧЕНИЕ(Документ.ЧекККМ.ПустаяСсылка).Дата >= &Дата1 И Дата <= &Дата2 лучше заменять на Дата МЕЖДУ &Дата1 И &Дата2 для улучшения читаемости кода.