Как обойти появление условия "NULL = &Параметр" в запросе макета компоновки данных (СКД) при объединении таблиц?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе на новые версии платформы 1С:Предприятие 8.3 (начиная с релизов 8.3.13 и выше) разработчики часто сталкиваются с неприятным поведением Системы компоновки данных (СКД). Отчеты, которые годами исправно работали и формировали корректные данные, внезапно начинают возвращать пустой результат. При детальном анализе сформированного СКД запроса в макете компоновщика обнаруживается странное паразитное условие в секции ГДЕ вида NULL = &Параметр или NULL = &П1.

В этой статье мы подробно разберем, почему компоновщик макета СКД начинает вести себя подобным образом, проанализируем внутренние механизмы платформы и рассмотрим четыре эффективных способа обхода этой проблемы.

Выясняем причину: почему изменилось поведение СКД?

Рассмотрим ситуацию изнутри. Изменение логики работы компоновщика макета СКД было задекларировано разработчиками 1С. Начиная с версии платформы 8.3.13, механизм автоматического заполнения доступных настроек при использовании конструкций ОБЪЕДИНИТЬ или ОБЪЕДИНИТЬ ВСЕ стал более агрессивным.

Если пользователь накладывает отбор на какое-то поле компоновки, система пытается применить этот отбор ко всем частям (веткам) объединения, где этот отбор теоретически возможен. Однако, если в одной из ветвей объединения данное поле физически отсутствует, не выбрано или возвращает значение NULL, СКД пытается сбалансировать условия во всех частях запроса. Для этого компоновщик принудительно подставляет в секцию ГДЕ "пустой" ветки условие сравнения с NULL.

По правилам работы СУБД и стандарту SQL, любое прямое сравнение значения с NULL с помощью оператора равенства (например, NULL = &П1) всегда возвращает результат UNKNOWN (ложь). В итоге вся ветка запроса, в которую СКД добавила такое условие, полностью перестает возвращать строки, даже если все остальные условия в ней абсолютно истинны.

Посмотрим на пример исходного запроса, который пишется разработчиком в схеме:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
{ВЫБРАТЬ
    Номенклатура.* КАК Номенклатура}
ИЗ
    Справочник.Номенклатура КАК Номенклатура
{ГДЕ
    Номенклатура.Ссылка.* КАК Номенклатура1,
    Номенклатура.Ссылка.* КАК Номенклатура}
 ОБЪЕДИНИТЬ ВСЕ
 ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
 ИЗ
    Справочник.Номенклатура КАК Номенклатура
{ГДЕ
    Номенклатура.Ссылка.* КАК Номенклатура2,
    Номенклатура.Ссылка.* КАК Номенклатура}

На старых версиях платформы (например, 8.3.7) при установке пользователем отборов по полям Номенклатура1 и Номенклатура2 система формировала вполне логичный и работоспособный итоговый запрос:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура = &П
 ОБЪЕДИНИТЬ ВСЕ
 ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
 ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура = &П1

Однако на платформе 8.3.13 этот же запрос преобразуется компоновщиком в следующую конструкцию:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    (Номенклатура = &П И NULL = &П1)
 ОБЪЕДИНИТЬ ВСЕ
 ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
 ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    (NULL = &П И Номенклатура = &П1)

Из-за условий NULL = &П1 в первой части и NULL = &П во второй части объединения итоговый запрос не вернет ни одной строки. Разберем по шагам, как можно обойти эту проблему.

---

Способ 1. Отключение автозаполнения и явное описание отборов (Рекомендуемый способ)

Начиная с версии платформы 8.3.14, разработчики исправили наиболее агрессивную часть этого механизма. Теперь поведение компоновщика напрямую зависит от флага "Автозаполнение" у набора данных-запроса в СКД.

Если флаг автозаполнения включен, система продолжит навязывать ошибочные условия с NULL. Но если мы отключим автозаполнение, компоновщик будет строго следовать только тем инструкциям, которые разработчик явно описал в фигурных скобках {}.

Выполним следующие шаги для настройки схемы:

  1. Откроем схему компоновки данных и перейдем на вкладку "Наборы данных".
  2. Выберем наш набор данных-запрос и снимем флажок "Автозаполнение".
  3. В тексте запроса самостоятельно и точечно опишем доступные поля отбора в фигурных скобках для каждой ветки объединения.

Пример корректного описания полей при выключенном автозаполнении:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Номенклатура
{ГДЕ
    Номенклатура.Ссылка.* КАК Номенклатура1}

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Номенклатура
{ГДЕ
    Номенклатура.Ссылка.* КАК Номенклатура2}

В этом случае, если пользователь установит отбор по полю Номенклатура1, условие будет добавлено СКД строго в первую часть объединения, а во вторую часть никакие паразитные условия вида NULL = &Параметр подставляться не будут.

---

Способ 2. Использование временных таблиц для изоляции условий

Если у вас сложная структура запроса с множеством объединений, и ручное отключение автозаполнения требует переписывания огромного количества полей на вкладках СКД, можно использовать классический обходной путь через временные таблицы.

Идея заключается в том, чтобы наложить компоновочные отборы СКД на этапе помещения данных во временные таблицы, а в финальном запросе пакета просто объединить уже отфильтрованные данные. В таком случае компоновщик макета применит условия к исходным таблицам и не сломает результирующее объединение.

Рассмотрим пример реструктуризации запроса:


// Помещаем первую порцию данных со своими отборами во временную таблицу
ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Часть1
ИЗ
    Справочник.Номенклатура КАК Номенклатура
{ГДЕ
    Номенклатура.Ссылка.* КАК Номенклатура1}
;

// Помещаем вторую порцию данных во вторую временную таблицу
ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Часть2
ИЗ
    Справочник.Номенклатура КАК Номенклатура
{ГДЕ
    Номенклатура.Ссылка.* КАК Номенклатура2}
;

// Объединяем готовые результаты без вмешательства СКД в финальный запрос
ВЫБРАТЬ
    Номенклатура
ИЗ
    ВТ_Часть1 КАК ВТ_Часть1

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Номенклатура
ИЗ
    ВТ_Часть2 КАК ВТ_Часть2

Этот способ является наиболее надежным и кроссплатформенным, так как он гарантированно работает на любых версиях платформы 1С:Предприятие 8.3, начиная с самых старых и заканчивая актуальными релизами.

---

Способ 3. Замена NULL на типизированные пустые ссылки в списке выбора

Иногда паразитные условия с NULL возникают из-за того, что в одной из частей ОБЪЕДИНИТЬ ВСЕ колонка физически присутствует, но заполнена литералом NULL (например, когда мы объединяем разные таблицы и выравниваем структуру их колонок).

Проанализируем пример "проблемного" списка выбора:


ВЫБРАТЬ
    Продажи.Номенклатура КАК Номенклатура,
    Продажи.Сумма КАК СуммаОборота
ИЗ
    РегистрНакопления.Продажи КАК Продажи

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Остатки.Номенклатура КАК Номенклатура,
    NULL КАК СуммаОборота // Источник потенциальной проблемы при отборе по Сумме
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Остатки

Чтобы избежать генерации сравнения с NULL в условиях ГДЕ, мы можем типизировать пустые значения в списке выбора, заменив литерал NULL на числовой ноль или пустую ссылку нужного типа.

Посмотрим, как изменить вторую часть запроса:


ВЫБРАТЬ
    Остатки.Номенклатура КАК Номенклатура,
    Выразить(0 Как Число(15, 2)) КАК СуммаОборота
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Остатки

Если речь идет о ссылочных типах данных, то вместо NULL в списке выбора можно использовать пустую ссылку конкретного справочника или документа:


ВЫБРАТЬ
    ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка) КАК Договор

При таком подходе СКД сгенерирует в макете стандартное сравнение с типизированным значением (например, Договор = &Параметр), что позволит СУБД корректно обработать условие без прерывания выборки.

---

Способ 4. Разделение запроса на разные наборы данных СКД

Если части объединения логически представляют собой независимые сущности (например, мы выбираем данные по продажам из одной таблицы и данные по закупкам из другой), хорошей практикой будет отказ от использования конструкции ОБЪЕДИНИТЬ ВСЕ внутри одного SQL-запроса.

Вместо этого мы можем использовать архитектурные возможности самой СКД:

  1. Создадим в схеме компоновки данных два отдельных набора данных-запроса: НаборДанныхПродажи и НаборДанныхЗакупки.
  2. Для каждого набора данных СКД сформирует свой собственный изолированный запрос.
  3. Свяжем эти наборы данных на вкладке "Связи наборов данных" или объединим их через настройки виртуальных полей в окне настроек отчета.

При таком подходе пользовательские отборы будут накладываться абсолютно независимо на каждый из наборов данных, полностью исключая вероятность взаимного влияния условий фильтрации друг на друга.

Резюме

Мы проанализировали специфику работы СКД на платформе 8.3.13 и последующих версиях. Появление условий NULL = &Параметр в макете — это следствие оптимизации работы компоновщика при автозаполнении настроек объединений. Чтобы ваш отчет работал стабильно, используйте отключение автозаполнения с явным указанием полей в фигурных скобках {} или переводите логику объединения на временные таблицы.

← На главную