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

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

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

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

Ограничения при загрузке таблицы значений в запрос

Когда мы передаем объект типа ТаблицаЗначений в качестве параметра запроса (например, &ТаблицаПараметр), платформа накладывает жесткие правила на первый запрос пакета. Очень частая ошибка начинающих разработчиков — попытка сразу прописать в нем секции ГДЕ, СГРУППИРОВАТЬ ПО или выполнить соединение.

Платформа 1С:Предприятие запрещает в запросе к таблице-параметру следующие конструкции:

  1. Секцию отбора ГДЕ.
  2. Любые соединения СОЕДИНЕНИЕ (левое, правое, полное, внутреннее).
  3. Агрегатные функции и группировки (СГРУППИРОВАТЬ ПО, СУММА, КОЛИЧЕСТВО).
  4. Конструкции РАЗЛИЧНЫЕ, ОБЪЕДИНИТЬ, УПОРЯДОЧИТЬ ПО и ИТОГИ.

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


ВЫБРАТЬ
    ВТ.ЗаказФилиала КАК ЗаказФилиала,
    ВТ.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ТаблицаПараметр
ИЗ
    &ТаблицаПараметр КАК ВТ
;

Все дальнейшие манипуляции (фильтрация, группировка, соединения с объектами базы данных) выполняются только на следующих этапах пакета запросов, обращаясь к уже созданной временной таблице ТаблицаПараметр.

Обязательная типизация колонок таблицы значений

Чтобы временная таблица корректно создалась в СУБД, переданная таблица значений должна быть строго типизирована. Если колонки созданы без указания типа или содержат смешанные типы данных (например, ссылку и строку), СУБД вернет ошибку.

Проанализируем два способа гарантировать правильные типы данных:

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

Посмотрим на пример безопасного помещения данных с явным приведением типов:


ВЫБРАТЬ
    ВЫРАЗИТЬ(Номенклатура.Ссылка КАК Справочник.Номенклатура) КАК Ссылка,
    ВЫРАЗИТЬ(Номенклатура.Наименование КАК СТРОКА(150)) КАК Наименование,
    ВЫРАЗИТЬ(Номенклатура.Артикул КАК СТРОКА(25)) КАК Артикул
ПОМЕСТИТЬ ВТ_Номенклатура
ИЗ
    &Номенклатура КАК Номенклатура
;

Особенности наложения отбора при левом соединении

Рассмотрим распространенную ошибку: требуется связать временную таблицу номенклатуры с регистром сведений НазначенияСвойствОбъектов и отобрать только определенные значения свойств. Если мы напишем условие в секции ГДЕ:


ИЗ
    ВТ_Номенклатура КАК ВТ
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НазначенияСвойствОбъектов КАК НазначенияСвойств
        ПО ВТ.Ссылка = НазначенияСвойств.Объект
ГДЕ
    НазначенияСвойств.ЗначениеСвойства = &НужноеЗначение

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

Разберем, как поступить в зависимости от бизнес-задачи:

  1. Если нужно оставить ВСЮ номенклатуру, а свойство вывести при наличии: переносим фильтр по свойству и значению непосредственно в условие соединения ПО.
    
    ИЗ
        ВТ_Номенклатура КАК ВТ
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НазначенияСвойствОбъектов КАК НазначенияСвойств
            ПО ВТ.Ссылка = НазначенияСвойств.Объект
            И НазначенияСвойств.Свойство = &Свойство
            И НазначенияСвойств.ЗначениеСвойства = &Значение
    
  2. Если нужно отобрать только позиции с заданным свойством: можно использовать секцию ГДЕ или заменить ЛЕВОЕ СОЕДИНЕНИЕ на ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
  3. Если нужно оставить номенклатуру без свойства ИЛИ с конкретным значением: используем конструкцию с проверкой на NULL в секции ГДЕ:
    
    ГДЕ
        НазначенияСвойств.ЗначениеСвойства = &Значение
        ИЛИ НазначенияСвойств.ЗначениеСвойства ЕСТЬ NULL
    

Обратите внимание на типы полей в условии соединения: соединять поля нужно строго по совпадению типов данных. Сравнение Номенклатура.Наименование = НазначенияСвойствОбъектов.Объект некорректно, так как поле Объект содержит ссылку (например, СправочникСсылка.Номенклатура), а Наименование — строку. Соединение всегда должно строиться по ссылочному полю: ВТ.Ссылка = НазначенияСвойств.Объект.

Универсальный отбор со списком значений

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


ГДЕ
    (&ВсеЗначения ИЛИ НазначенияСвойств.ЗначениеСвойства В (&СписокЗначений))

В программном коде передача параметров будет выглядеть следующим образом:


Запрос.УстановитьПараметр("СписокЗначений", МассивЗначений);
Запрос.УстановитьПараметр("ВсеЗначения", МассивЗначений.Количество() = 0);

Если переданный массив пуст, флаг &ВсеЗначения принимает значение Истина, и условие после ИЛИ даже не вычисляется СУБД — в выборку попадут все записи без ограничений.

Индексация и освобождение ресурсов СУБД

Для обеспечения высокого быстродействия запросов важно соблюдать два регламентных правила при работе с временными таблицами:

  1. Индексация полей соединения: если созданная временная таблица участвует в соединениях с другими таблицами или регистрами, обязательно добавляйте инструкцию ИНДЕКСИРОВАТЬ ПО. Индексировать следует те поля, которые участвуют в условии ПО.
    
    ВЫБРАТЬ
        ВТ.Ссылка КАК Ссылка
    ПОМЕСТИТЬ ВТ_Номенклатура
    ИЗ
        &ТаблицаПараметр КАК ВТ
    ИНДЕКСИРОВАТЬ ПО
        Ссылка
    ;
    
  2. Уничтожение временных таблиц: временные таблицы физически хранятся в базе данных tempdb СУБД. При использовании объекта МенеджерВременныхТаблиц они сохраняются в памяти до момента уничтожения менеджера. Завершайте тяжелые пакетные запросы явной инструкцией УНИЧТОЖИТЬ ИмяТаблицы, чтобы освобождать дисковое пространство и оперативную память сервера.
← На главную