При разработке запросов к регистрам накопления или сведений в платформе 1С разработчики часто сталкиваются с необходимостью наложить отбор по реквизиту измерения. Классический пример: получение остатков только по тем договорам, у владельцев которых снят признак НеПоказыватьВоВзаиморасчетах. Запись такого условия прямо в параметрах виртуальной таблицы через точку (НЕ Договор.Владелец.НеПоказыватьВоВзаиморасчетах) способна существенно замедлить выполнение запроса. Разберем подробно, почему так происходит и как правильно оптимизировать данный алгоритм.
Проанализируем механизм работы платформы и СУБД при вычислении итогов виртуальной таблицы:
Договор.Владелец.Реквизит заставляет платформу формировать соединение таблиц справочников непосредственно внутри подзапроса, вычисляющего срез итогов регистра.NULL. Выражение НЕ NULL в SQL оценивается как UNKNOWN (ложь), из-за чего такие строки будут неожиданно исключены из выборки.Стандарты разработки 1С предписывают исключать обращение к реквизитам через точку в параметрах виртуальных таблиц. Наиболее эффективный подход заключается в предварительной выборке разрешенных ссылок во временную таблицу с ее последующей индексацией.
Разберем по шагам построение такого запроса:
ИНДЕКСИРОВАТЬ ПО для поля связи, чтобы СУБД могла соединять таблицы максимально быстро.В (ВЫБРАТЬ ...) из созданной временной таблицы.Посмотрим на пример правильной структуры пакетного запроса:
ВЫБРАТЬ
Договоры.Ссылка КАК Договор
ПОМЕСТИТЬ ВТ_ОтфильтрованныеДоговоры
ИЗ
Справочник.ДоговорыКонтрагентов КАК Договоры
ГДЕ
ЕСТЬNULL(Договоры.Владелец.НеПоказыватьВоВзаиморасчетах, ЛОЖЬ) = ЛОЖЬ
ИНДЕКСИРОВАТЬ ПО
Договор
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Взаиморасчеты.Договор КАК Договор,
Взаиморасчеты.СуммаОстаток КАК СуммаОстаток
ИЗ
РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(
&Период,
Договор В (ВЫБРАТЬ ВТ.Договор ИЗ ВТ_ОтфильтрованныеДоговоры КАК ВТ)
) КАК Взаиморасчеты
Если контрагентов в базе существенно меньше, чем договоров, мы можем отбирать во временную таблицу непосредственно владельцев (контрагентов). Однако в этом случае в регистре должно присутствовать отдельное измерение Контрагент, по которому и будет выполняться фильтрация в виртуальной таблице.
Обратим внимание на то, как обрабатывается условие отрицания флага. Прямое сравнение Поле = ЛОЖЬ или НЕ Поле может отсечь строки, где значение не определено или равно NULL (например, поврежденные ссылки или договоры без владельца).
Чтобы исключить потерю данных и избежать ошибок выполнения, рекомендуется явно обрабатывать возможные неопределенные значения с помощью функции ЕСТЬNULL:
ГДЕ
ЕСТЬNULL(Договоры.Владелец.НеПоказыватьВоВзаиморасчетах, ЛОЖЬ) = ЛОЖЬ
Такая конструкция гарантирует, что если у договора отсутствует владелец или реквизит не заполнен, строка будет интерпретирована как разрешенная для взаиморасчетов.
Рассмотрим альтернативные варианты, которые часто предлагают на практике, и выясним их недостатки:
ГДЕ основного запроса: Если убрать фильтр из параметров виртуальной таблицы и поместить его в общее ГДЕ, виртуальная таблица сначала рассчитает итоги по абсолютно всем договорам базы данных, и только затем отбросит лишние строки. Это оправдано только в редких случаях, когда общих записей итогов в регистре крайне мало, но на крупных рабочих базах создает избыточную нагрузку на дисковую подсистему и память сервера.&МассивДоговоров): Выборка списка ссылок в коде встроенного языка и передача массива через параметр УстановитьПараметр() допустима только при гарантированно малом объеме данных (до нескольких сотен элементов). Если список превышает 1000–2000 записей, СУБД (MS SQL Server, PostgreSQL) не сможет построить эффективный план выполнения из-за отсутствия статистики распределения, а также возрастает риск превышения лимитов на размер текста запроса и число параметров.Таким образом, использование проиндексированной временной таблицы в рамках одного пакетного запроса является наиболее универсальным, производительным и безопасным решением для оптимизации условий виртуальных таблиц.