Почему не работает внутреннее соединение по незаполненным полям составного типа в 1С и как это исправить?

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

При разработке запросов в платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда внутреннее соединение двух таблиц по измерению или реквизиту составного типа возвращает пустой результат или теряет строки, где поле не заполнено. На первый взгляд кажется, что если в обеих таблицах значение отсутствует, то условие равенства должно выполниться. Однако на уровне СУБД и платформы сравнение таких полей имеет свои особенности.

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

Причины некорректного соединения по составным полям

Чтобы понять, почему условие соединения вида ВыборкаСписания.Заказ = ОстаткиЗаказовПоставщикам.Заказ не связывает пустые строки, рассмотрим физическую структуру хранения данных:

  1. Расщепление составного ссылочного типа в СУБД. В базе данных поле составного типа хранится не в одной колонке, а разделяется на несколько физических полей: признак базового типа, номер таблицы объекта метаданных (вид ссылки) и уникальный идентификатор (GUID). При выполнении сравнения в SQL платформа сопоставляет все три составляющие.
  2. Разнородность «пустых» значений. В платформе 1С незаполненное поле может принимать разные значения:
    1. Неопределено — тип не выбран вовсе;
    2. NULL — значение отсутствует физически (например, при левом соединении или нетипизированной временной таблице);
    3. Пустая ссылка конкретного типа — например, Документы.ЗаказПокупателя.ПустаяСсылка() или Документы.ЗаказНаПроизводство.ПустаяСсылка().
    Пустая ссылка одного документа не равна пустой ссылке другого документа, а также не равна значению Неопределено или NULL, так как их бинарные коды и номера таблиц в СУБД различаются.
  3. Нетипизированные колонки в ТаблицеЗначений. Если колонка ТаблицаЗначений передается в запрос в качестве параметра без явного указания ОписаниеТипов, во временной таблице типы данных могут сформироваться некорректно, а пустые значения превратятся в NULL. В стандарте SQL операция сравнения NULL = NULL всегда дает результат «Ложь».

Способ 1. Явная типизация колонок ТаблицыЗначений перед передачей в запрос

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

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


МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("ДокументСсылка.ЗаказПокупателя"));
МассивТипов.Добавить(Тип("ДокументСсылка.ЗаказНаПроизводство"));

ОписаниеТипа = Новый ОписаниеТипов(МассивТипов);
ВремТЗ.Колонки.Добавить("Заказ", ОписаниеТипа);

Также можно получить точное описание типа напрямую из метаданных конфигурации:


ОписаниеТипа = Метаданные.РегистрыНакопления.ЗаказыПоставщикамПоЗаказам.Измерения.Заказ.Тип;
ВремТЗ.Колонки.Добавить("Заказ", ОписаниеТипа);

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

Способ 2. Унификация значений в запросе через конструкцию ВЫБОР

Если данные уже поступили в запрос с неоднородными пустыми значениями (смесь пустых ссылок разных документов и Неопределено), их можно привести к общему фиктивному значению для выполнения соединения, а затем вернуть обратно.

Рассмотрим пример подготовки связываемых полей с приведением всех видов пустых значений к пустой строке:


ВЫБРАТЬ
    ВЫБОР
        КОГДА ВремТЗ.Заказ = ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка)
            ИЛИ ВремТЗ.Заказ = ЗНАЧЕНИЕ(Документ.ЗаказНаПроизводство.ПустаяСсылка)
            ИЛИ ВремТЗ.Заказ = НЕОПРЕДЕЛЕНО
            ТОГДА ""
        ИНАЧЕ ЕСТЬNULL(ВремТЗ.Заказ, "")
    КОНЕЦ КАК Заказ,
    ВремТЗ.ЗаказПоставщику КАК ЗаказПоставщику,
    ВремТЗ.Номенклатура КАК Номенклатура,
    ВремТЗ.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры
ПОМЕСТИТЬ ВыборкаСписания
ИЗ
    &ВремТЗ КАК ВремТЗ
;

После выполнения выборки остатков в результирующем запросе пустое значение при необходимости конвертируется обратно в НЕОПРЕДЕЛЕНО.

Способ 3. Передача условий в параметры виртуальной таблицы

Для оптимизации быстродействия рекомендуется не соединять временную таблицу через ВНУТРЕННЕЕ СОЕДИНЕНИЕ, а передавать условие непосредственно в параметры виртуальной таблицы остатков через конструкцию В.

Разберем пример такого запроса:


ВЫБРАТЬ
    ВремТЗ.Заказ КАК Заказ,
    ВремТЗ.ЗаказПоставщику КАК ЗаказПоставщику,
    ВремТЗ.Номенклатура КАК Номенклатура,
    ВремТЗ.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры
ПОМЕСТИТЬ ВыборкаСписания
ИЗ
    &ВремТЗ КАК ВремТЗ
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Остатки.ЗаказПоставщику КАК ЗаказПоставщику,
    Остатки.Заказ КАК Заказ,
    Остатки.Номенклатура КАК Номенклатура,
    Остатки.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
    Остатки.КоличествоОстаток КАК КоличествоОстаток,
    Остатки.СуммаВсегоОстаток КАК СуммаВсегоОстаток
ИЗ
    РегистрНакопления.ЗаказыПоставщикамПоЗаказам.Остатки(
            &ДатаСреза,
            (Заказ, ЗаказПоставщику, Номенклатура, ХарактеристикаНоменклатуры) В
                (ВЫБРАТЬ
                    Выборка.Заказ,
                    Выборка.ЗаказПоставщику,
                    Выборка.Номенклатура,
                    Выборка.ХарактеристикаНоменклатуры
                ИЗ
                    ВыборкаСписания КАК Выборка)) КАК Остатки

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

Способ 4. Разделение запроса через ОБЪЕДИНИТЬ ВСЕ по типам документов

Если в регистре содержатся пустые ссылки определенных типов, а в исходной таблице передается Неопределено, наиболее производительным решением в высоконагруженных системах является разделение выборки на отдельные ветки с использованием оператора ССЫЛКА:

  1. В первой ветке соединяем записи, где Заказ ССЫЛКА Документ.ЗаказПокупателя;
  2. Во второй ветке обрабатываем Заказ ССЫЛКА Документ.ЗаказНаПроизводство;
  3. В отдельной ветке соединяем записи с незаполненными заказами, где Заказ = НЕОПРЕДЕЛЕНО или Заказ ЕСТЬ NULL.

Такой подход исключает сложные условия в секции ПО и позволяет СУБД строить оптимальные планы выполнения запросов.

← На главную