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