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