Часто при разработке на платформе 1С мы сталкиваемся с ситуацией, когда результат выполнения сложного запроса в динамическом списке на форме отличается от того, что мы видим в консоли запросов. Проанализируем эту ситуацию и разберем по шагам, почему это происходит и как с этим бороться.
Посмотрим на классический пример. Допустим, мы пишем сложный запрос, в котором собираем остатки во временную таблицу. Исходный код выглядит примерно так:
ВЫБРАТЬ
ВТ.Организация КАК Организация,
ВТ.Номенклатура КАК Номенклатура,
ВТ.Характеристика КАК Характеристика,
ВТ.Склад КАК Склад,
ВТ.Количество КАК Количество,
ВТ.Резерв КАК Резерв
ПОМЕСТИТЬ ВТ_ОстаткиПредв
После этого временная таблица используется в соединениях для получения итогового результата. Однако, если мы посмотрим на реальный исполняемый код через анализатор запросов, мы можем с удивлением обнаружить, что платформа удалила часть полей (например, Организация, Склад или Характеристика). В итоге система группирует записи без учета этих разрезов, и мы получаем некорректные или задвоенные остатки.
Выясним причину. Это поведение не является ошибкой платформы 1С. Мы имеем дело со штатным механизмом оптимизации Системы Компоновки Данных (СКД). Так как динамические списки работают на базе СКД, встроенный оптимизатор анализирует итоговые настройки списка. Если он видит, что какие-то поля не выводятся на форму пользователю и не участвуют в отборах, он физически "вырезает" их из текста SQL-запроса для ускорения выборки данных. К сожалению, оптимизатор не всегда идеально оценивает влияние удаленного поля на группировку строк. Рассмотрим несколько способов, как мы можем управлять этим процессом и заставить СКД работать так, как нам нужно.
Самый простой и очевидный метод, который мы рассмотрим в первую очередь — это использование настроек самого динамического списка. В свойствах реквизита списка для каждого поля предусмотрен специальный флажок.
Это заставит систему всегда включать данное поле в выборку. Однако проанализируем побочные эффекты этого метода:
Во-первых, мы получаем снижение производительности. Поле будет считываться из базы данных и передаваться с сервера на клиент для абсолютно каждой строки, даже если пользователь выключил эту колонку в пользовательских настройках видимости списка.
Во-вторых, могут возникнуть программные ошибки при работе с ТекущиеДанные. Если пользователь настроит в списке свою собственную группировку, то при выделении строки-группировки обращение к таким полям может вернуть Неопределено или вызвать ошибку, так как для строки-группировки детального поля физически не существует. Это обязательно нужно учитывать при написании обработчиков событий формы.
Разберем более архитектурно правильный метод. Мы можем дать явную команду оптимизатору на уровне самого текста запроса. Чтобы система не удаляла поле при формировании временной таблицы, используем расширения языка запросов СКД со знаком звездочки.
Непосредственно в запросе временной таблицы нам нужно прописать поля внутри фигурных скобок:
{ВЫБРАТЬ
Организация.*,
Склад.*
}
Этот синтаксис жестко сигнализирует платформе, что указанные поля должны присутствовать в выборке независимо от итоговых настроек видимости списка у пользователя.
Рассмотрим радикальный способ. Если запрос очень сложный и оптимизатор СКД ломает логику в нескольких местах, мы можем полностью отключить автоматическое формирование полей.
Для этого в свойствах динамического списка нужно снять флажок Автозаполнение. В этом случае система прекратит вмешиваться в структуру выборки. Однако нам придется вручную, используя язык расширений СКД, прописать все необходимые доступные поля, отборы и параметры, которые пользователь сможет использовать в настройках.
Посмотрим на практические трюки, к которым часто прибегают разработчики. Суть метода заключается в том, чтобы запутать оптимизатор или создать для него искусственную зависимость.
Мы можем добавить в последний запрос нейтральное выражение отбора с обращением к проблемному полю. Например:
ГДЕ НЕ ВТ.Склад ЕСТЬ NULL
Второй вариант этого трюка — обернуть проблемное поле в функцию непосредственно в блоке выбора:
ВЫБРАТЬ
ЕСТЬNULL(ВТ.Склад, ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)) КАК Склад
Или использовать приведение типов:
ВЫБРАТЬ
ВЫРАЗИТЬ(ВТ.Склад КАК Справочник.Склады) КАК Склад
Когда к полю применяется функция трансформации данных, оптимизатор платформы с гораздо меньшей вероятностью рискнет его исключить, так как это сильно усложняет алгоритм предсказания связей между таблицами.
Если мы работаем напрямую со схемой СКД (например, в отчетах или при сложной программной генерации списков), мы можем управлять поведением через роли полей. Для реквизитов, влияющих на гранулярность виртуальных таблиц, необходимо правильно задавать роль.
Указание роли Измерение для таких полей, как Склад или Характеристика, сообщает системе компоновки, что удалять данное поле нельзя ни при каких обстоятельствах. Оно отвечает за правильный разрез итогов, и его исключение приведет к математически неверным остаткам. Система это понимает и сохраняет поле в итоговом SQL-запросе.
Применяя эти методы, мы сможем полностью контролировать формирование запросов в динамических списках и избегать проблем с исчезающими данными и некорректными остатками на формах платформы 1С.