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