Параметры виртуальной таблицы vs ГДЕ
Одно и то же условие может сделать запрос молниеносным или мучительно медленным — всё решает, куда его поставить: в параметры виртуальной таблицы или в ГДЕ.
Правило: условие в параметрах виртуальной таблицы применяется до расчёта итогов — платформа поднимает из базы только нужные строки. То же условие в
ГДЕприменяется после — итоги считаются по всем данным, а лишнее отбрасывается в конце.
Это, пожалуй, самая частая ошибка производительности у новичков в запросах 1С. Запрос даёт правильный результат в обоих случаях, поэтому на маленькой базе разницы не видно. Но когда в регистре сотни тысяч записей, вариант с условием в ГДЕ начинает «висеть», а точно такой же запрос с условием в параметрах отрабатывает мгновенно. Разберём, почему так, на живом сравнении.
Зачем это нужно на практике
Отчёты и обработки в 1С почти всегда строятся на виртуальных таблицах Остатков и Оборотов по большим регистрам. Неправильно поставленный отбор — самая распространённая причина того, что «отчёт по одному складу за один день считается минуту». Понимание этого правила отличает джуна, у которого отчёты тормозят, от разработчика, у которого они летают, — и его почти всегда спрашивают на собеседовании.
Медленно: условие в ГДЕ
Пусть нужен остаток товаров по одному складу. Наивный вариант — взять виртуальную таблицу Остатки по всем складам, а нужный склад отобрать в ГДЕ:
// МЕДЛЕННО — отбор по складу в ГДЕ
ВЫБРАТЬ
ОстаткиТоваров.Номенклатура КАК Номенклатура,
ОстаткиТоваров.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&НаДату, ) КАК ОстаткиТоваров
ГДЕ
ОстаткиТоваров.Склад = &Склад
Что делает платформа: сначала виртуальная таблица Остатки(&НаДату, ) с пустым отбором считает остатки по всем складам и всей номенклатуре — это тяжёлый расчёт по итогам всего регистра. И только потом ГДЕ ОстаткиТоваров.Склад = &Склад выбрасывает 95% посчитанного. Мы заставили СУБД сделать огромную работу, чтобы почти всё выкинуть.
Быстро: условие в параметрах виртуальной таблицы
Правильный вариант — передать отбор внутрь виртуальной таблицы, во второй параметр:
// БЫСТРО — отбор по складу в параметрах виртуальной таблицы
ВЫБРАТЬ
ОстаткиТоваров.Номенклатура КАК Номенклатура,
ОстаткиТоваров.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(
&НаДату,
Склад = &Склад) КАК ОстаткиТоваров
Теперь платформа знает про ограничение заранее и считает остатки только по одному складу — по индексу, поднимая из базы в разы меньше строк. Результат тот же, но работа несопоставимо меньше. Разница на большом регистре — секунды против долей секунды.
Мнемоника: отбор по измерениям — в параметры виртуальной таблицы (Склад, Организация, Номенклатура, ТипЦен). А вот условие по рассчитанному ресурсу (
КоличествоОстаток > 0) поставить в параметры нельзя — ресурс ещё не посчитан на входе, поэтому такое условие законно живёт в ГДЕ.
Полный пример из кода
Так выглядит грамотный запрос остатков по складу с отбросом нулей. Отбор по измерению (Склад) — в параметрах, условие по ресурсу (остаток больше нуля) — в ГДЕ:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОстаткиТоваров.Номенклатура КАК Номенклатура,
| ОстаткиТоваров.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(
| &НаДату,
| Склад = &Склад) КАК ОстаткиТоваров
|ГДЕ
| ОстаткиТоваров.КоличествоОстаток > 0
|УПОРЯДОЧИТЬ ПО
| Количество УБЫВ";
Запрос.УстановитьПараметр("НаДату", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("Склад", ОсновнойСклад);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Номенклатура.Наименование + ": " + Выборка.Количество);
КонецЦикла;
Результат:
Мышь Gamma: 220
Смартфон Alpha: 14
Ноутбук Beta: 3
То же правило для Оборотов и СрезПоследних
Принцип общий для всех виртуальных таблиц. Отбор по номенклатуре в Оборотах тоже кладут в параметры:
ВЫБРАТЬ
ПродажиОбороты.Номенклатура КАК Номенклатура,
ПродажиОбороты.СуммаОборот КАК Выручка
ИЗ
РегистрНакопления.Продажи.Обороты(
&Начало, &Конец, ,
Номенклатура В (&СписокТоваров)) КАК ПродажиОбороты
У Оборотов отбор — четвёртый параметр (после начала, конца и периодичности), у Остатков и СрезПоследних — второй. Оператор В (&СписокТоваров) отлично работает и в параметрах виртуальной таблицы — можно ограничить расчёт заранее заданным списком.
Как это работает
Чтобы прочувствовать разницу, полезно представить порядок, в котором платформа собирает запрос. Виртуальная таблица — это не готовая таблица, а инструкция «посчитай итоги вот так». Условие в её параметрах платформа вклеивает внутрь этой инструкции: сгенерированный SQL получает ограничение ещё до агрегации, СУБД идёт по индексу измерения и складывает только отобранные движения. Это то, что называют «ранняя фильтрация» (predicate pushdown) — отбор проталкивается как можно ближе к источнику данных.
Условие же в ГДЕ навешивается снаружи, поверх уже посчитанной виртуальной таблицы. Сначала СУБД честно агрегирует итоги по всему объёму, формирует промежуточный результат — и лишь затем фильтрует его. Чем больше записей в регистре, тем дороже обходится этот «посчитать всё, потом выбросить лишнее». Отсюда и правило: всё, что можно сказать таблице заранее (отбор по измерениям), говорите в её параметрах.
Частые ошибки
- «Работает же — и ладно». На тестовой базе из сотни записей оба варианта мгновенны, разница прячется. Проблема вылезает в бою на реальном объёме. Привыкайте писать правильно сразу, а не «когда затормозит».
- Пытаться отобрать ресурс в параметрах.
Остатки(&НаДату, КоличествоОстаток > 0)— так нельзя: на входе виртуальной таблицы ресурс ещё не посчитан. Отбор по измерению — в параметры, условие по ресурсу — в ГДЕ. - Соединять с большой виртуальной таблицей без отбора. Если соединить справочник с
Остатки(&НаДату, )без ограничения, платформа посчитает остатки по всему регистру ради нескольких строк соединения. Ограничивайте виртуальную таблицу в параметрах ещё до соединения. - Дублировать отбор и там, и там. Ставить одно и то же условие сразу в параметры и в ГДЕ не нужно — достаточно параметров. ГДЕ оставляйте только для того, что нельзя выразить в параметрах (условия по ресурсам, сложная логика по вычисляемым полям).
- Путать порядок параметров. У Оборотов отбор идёт четвёртым аргументом; если по ошибке передать его третьим, платформа воспримет его как периодичность и выдаст ошибку или неверный результат. Сверяйтесь с конструктором запроса.
Итоги
- Отбор по измерениям кладите в параметры виртуальной таблицы — он применяется до расчёта итогов и работает быстро.
- То же условие в
ГДЕсчитает итоги по всем данным и отбрасывает лишнее в конце — на больших регистрах это медленно. - Условие по рассчитанному ресурсу (
КоличествоОстаток > 0) в параметры поставить нельзя — оно законно живёт в ГДЕ. - Правило общее для Остатков, Оборотов, ОстаткиИОбороты и СрезПоследних; отличается лишь номер параметра-отбора.
- Результат одинаков в обоих случаях — разница только в скорости, и она огромна на реальных объёмах.