При разработке на платформе 1С:Предприятие 8 программисты часто сталкиваются с предупреждением статических анализаторов кода (например, BSL Language Server): «Обращение к виртуальной таблице без параметров» (VirtualTableCallWithoutParameters). На первый взгляд может показаться, что это лишь косметическое замечание, однако за ним стоят глубокие механизмы оптимизации работы СУБД и платформенные особенности хранения данных. Рассмотрим подробно, почему возникает эта ошибка, как она влияет на производительность и какими способами её можно устранить, сохранив правильную бизнес-логику.
Когда мы пишем запрос к виртуальной таблице, например, РегистрСведений.МойРегистр.СрезПоследних, платформа 1С транслирует этот вызов в сложный SQL-запрос. Виртуальная таблица не существует в базе данных в готовом виде (за исключением некоторых случаев с итогами), она формируется «на лету».
Проанализируем разницу в подходах фильтрации данных:
ГДЕ, платформа сначала заставит СУБД построить виртуальную таблицу (часто для всех записей регистра), и только потом применит фильтр. Это приводит к избыточному чтению данных с диска, перерасходу оперативной памяти и существенному замедлению работы системы.Рассмотрим пример кода, который вызывает диагностику ошибки:
ВЫБРАТЬ
МойРегистр.Измерение КАК Измерение
ИЗ
РегистрСведений.МойРегистр.СрезПоследних КАК МойРегистр
ГДЕ
МойРегистр.Ресурс = &Ресурс
Для исправления этой ситуации, согласно стандартам, следует перенести условия в параметры виртуальной таблицы:
ВЫБРАТЬ
МойРегистр.Измерение КАК Измерение
ИЗ
РегистрСведений.МойРегистр.СрезПоследних(, Ресурс = &Ресурс) КАК МойРегистр
Важно понимать, что для регистров сведений в 1С существует специальная опция «Разрешить итоги: Срез последних». Рассмотрим, как это влияет на выбор между скоростью и чистотой кода.
Выясним причину, по которой иногда разработчики намеренно не указывают дату в параметрах. Если флаг итогов включен, платформа создает в СУБД отдельную физическую таблицу, где всегда хранится только актуальный срез на текущий момент.
Проанализируем две ситуации:
Таким образом, возникает дилемма: использовать параметр Период, чтобы соответствовать стандарту и застраховаться от будущих записей, или не использовать его ради максимальной производительности. Если ваша бизнес-логика гарантирует отсутствие записей «будущим числом», вызов без даты оправдан.
Стандарт VirtualTableCallWithoutParameters требует передавать в параметры не только период, но и отборы по измерениям. Рассмотрим на примере поиска транспортного средства для водителя.
Если мы используем отбор в ГДЕ, СУБД может выполнить полное сканирование (Table Scan) всей таблицы актуальных записей. Если же мы передаем измерение в параметр, СУБД выполняет точечный поиск по индексу (Index Seek).
Правильный вариант вызова с точки зрения производительности и стандартов:
// В параметрах указываем пустую дату (для использования итогов)
// и конкретное измерение для быстрого поиска по индексу
Запрос.Текст =
"ВЫБРАТЬ
| Регистр.ТранспортноеСредство
|ИЗ
| РегистрСведений.ЗакреплениеВодителей.СрезПоследних(, Водитель = &Водитель) КАК Регистр";
В ходе обсуждения часто возникает вопрос: что делать, если нужно вернуть пустой результат, если последняя запись в регистре означает «снятие» объекта (например, водитель больше не числится за машиной)?
Разберем ситуацию: если мы добавим условие в параметры СрезПоследних(, Водитель = &Водитель И ТранспортноеСредство <> ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.ПустаяСсылка)), то запрос может вернуть предыдущую запись, где машина была указана, хотя последняя запись была пустой. Это логическая ошибка.
Правильный подход к решению такой задачи:
Рассмотрим исключения, когда проверку анализатора можно и нужно подавить:
Для подавления предупреждения в BSL Language Server используйте специальные комментарии:
// BSLLS:VirtualTableCallWithoutParameters-off
Результат = Запрос.Выполнить();
// BSLLS:VirtualTableCallWithoutParameters-on
Подведем итог нашему анализу и сформулируем правила работы с виртуальными таблицами:
ГДЕ — на фильтрацию уже готового результата.Соблюдение этих правил позволит вам писать не только «чистый» код, соответствующий стандартам, но и создавать высокопроизводительные решения, которые будут стабильно работать даже на крупных базах данных.