Параметры виртуальных таблиц против ГДЕ
«Вот запрос к остаткам. Как ускорить его, не меняя результат?» — и половина кандидатов проваливается на этом вопросе.
Виртуальная таблица — это не реальная таблица в базе, а «представление», которое платформа 1С разворачивает в сложный SQL-запрос по вашим параметрам в момент выполнения. Условие, переданное в её параметры, попадает внутрь этого разворота; то же условие в
ГДЕприменяется уже к готовому результату.
Это один из любимых вопросов на производительность на собеседовании 1С-разработчика. Спрашивают его потому, что ответ мгновенно показывает, понимает ли человек, что запрос 1С — это не то же самое, что таблица в базе, и что за красивым языком запросов скрывается реальный SQL, который платформа генерирует за вас.
Вопрос-крючок
Дают два запроса, которые возвращают одинаковый результат — остатки товаров на складе «Основной» — и просят сказать, какой быстрее и почему. Вот «медленный» вариант, где условие вынесено в ГДЕ:
ВЫБРАТЬ
ОстаткиТоваров.Номенклатура,
ОстаткиТоваров.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки() КАК ОстаткиТоваров
ГДЕ
ОстаткиТоваров.Склад = &СкладА вот «быстрый» — то же условие, но переданное в параметры виртуальной таблицы:
ВЫБРАТЬ
ОстаткиТоваров.Номенклатура,
ОстаткиТоваров.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК ОстаткиТоваровВторой параметр метода Остатки() — это условие отбора (первый параметр — момент времени, поэтому перед запятой пусто). Оба запроса вернут одно и то же. Но работают они принципиально по-разному.
В чём разница на самом деле
Ключ к ответу — понять, когда применяется отбор.
Условие в ГДЕ: сначала посчитать всё, потом отсеять
Регистр остатков внутри устроен как две таблицы: таблица движений (приходы и расходы) и таблица итогов (заранее свёрнутые остатки на границах периодов). Чтобы получить остаток на текущий момент, платформа берёт ближайший рассчитанный итог и «докручивает» его движениями. Когда условие стоит в ГДЕ, платформа сначала считает остатки по всем складам сразу, разворачивает всю эту тяжёлую конструкцию, и только потом из готового результата выбрасывает строки, где склад не «Основной».
Условие в параметрах: считать сразу только нужное
Когда тот же отбор передан в параметры, платформа встраивает его внутрь генерируемого SQL — в подзапросы к таблицам движений и итогов. Она считает остатки сразу только по складу «Основной». Меньше строк читается с диска, меньше данных сворачивается, меньше промежуточный результат. Особенно заметно на регистрах с миллионами движений и десятками складов: разница может быть в десятки раз.
Как отвечать
Хороший ответ звучит так: «Условие в параметрах виртуальной таблицы платформа встраивает внутрь SQL-запроса, который она генерирует для расчёта остатков, поэтому лишние данные вообще не читаются и не сворачиваются. Условие в ГДЕ применяется уже к готовому результату — платформа сперва рассчитывает остатки по всем складам, а потом отсеивает ненужное. Результат одинаковый, но во втором случае мы заставляем СУБД делать лишнюю работу. Поэтому всё, что можно, я выношу в параметры виртуальной таблицы.»
Если хотят копнуть глубже, добавьте, что посмотреть реальный SQL можно в технологическом журнале или через план запроса в консоли запросов — там видно, как отбор из параметров превращается в условие внутри вложенного SELECT, а не в наружный WHERE поверх всего.
И честная оговорка, которая всегда производит хорошее впечатление: «Есть нюанс — отбор в параметрах работает по измерениям регистра. Если поле не измерение, а, например, реквизит номенклатуры, в параметры его не передать, и такое условие всё равно уйдёт в ГДЕ.» Для остатков в параметры имеет смысл выносить именно измерения (Склад, Номенклатура, Организация).
Ещё пример — регистр сведений
Тот же принцип работает и для СрезПоследних() у периодического регистра сведений. Хотим цены только по одному виду цен на дату:
ВЫБРАТЬ
Цены.Номенклатура,
Цены.Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &ВидЦены) КАК ЦеныПервый параметр СрезПоследних() — дата среза, второй — условие отбора. Отбор по ВидЦены внутри параметров означает, что срез последних значений платформа строит сразу только по нужному виду цен, а не по всем видам с последующим отсевом.
Частые ошибки на собеседовании
Чего не стоит говорить (и делать в реальном коде):
- «Разницы нет, результат-то одинаковый». Результат одинаковый, а нагрузка на СУБД — нет. Это и есть суть вопроса.
- Путать параметры и
ГДЕместами. Помните: параметры — это то, что в скобках после имени виртуальной таблицы; они сужают выборку до расчёта. - Сначала соединить остатки с чем-то, а потом фильтровать по складу. Тогда до фильтра платформа посчитает остатки по всем складам и ещё и соединит их — двойная лишняя работа.
- Игнорировать индексы и типы полей. Отбор в параметрах ускоряет, но если в него передать не измерение, эффекта не будет — условие всё равно применится снаружи.
Итоги-шпаргалка
- Виртуальная таблица (
Остатки(),СрезПоследних(),Обороты()) разворачивается платформой в реальный SQL по вашим параметрам. - Условие в параметрах встраивается внутрь этого SQL — читается и сворачивается только нужное.
- Условие в
ГДЕприменяется к уже готовому результату — сначала считается всё, потом отсеивается. - В параметры выносим измерения регистра; условия по реквизитам через точку останутся в
ГДЕ. - Формула ответа: «одинаковый результат, но параметры экономят работу СУБД, потому что фильтруют до расчёта, а не после».