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