Почему при использовании ОБЪЕДИНИТЬ ВСЕ в запросе 1С появляются значения NULL?

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

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

Проанализируем природу этой проблемы, выясним, как платформа 1С и СУБД обрабатывают строковые данные и ссылки, и разберем корректные способы решения задачи.

В чем заключается скрытая причина появления NULL

Рассмотрим типовой сценарий, в котором проявляется такое поведение. В менеджере временных таблиц формируются две таблицы, поля которых на уровне кода 1С кажутся одинаковыми строками:


ВЫБРАТЬ
    ВременнаяТаблица.Показатель,
    ВременнаяТаблица.Сумма,
    ВременнаяТаблица.СсылкаНаДокумент
ИЗ
    ВременнаяТаблица1 КАК ВременнаяТаблица

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

ВЫБРАТЬ
    ВременнаяТаблица.Показатель,
    ВременнаяТаблица.Сумма,
    ВременнаяТаблица.СсылкаНаДокумент
ИЗ
    ВременнаяТаблица2 КАК ВременнаяТаблица

Если поле СсылкаНаДокумент в исходных временных таблицах было сформировано с помощью конструкции ПРЕДСТАВЛЕНИЕ(Ссылка) или ПРЕДСТАВЛЕНИЕССЫЛКИ(Ссылка), то во временную таблицу помещается вовсе не обычная примитивная строка.

Разберем внутренний механизм работы функции ПРЕДСТАВЛЕНИЕ() в платформе 1С:

  1. Функция ПРЕДСТАВЛЕНИЕ() не преобразует данные в тип NVARCHAR или VARCHAR на уровне СУБД в момент создания временной таблицы. Вместо этого платформа помещает в физическую таблицу СУБД набор служебных полей, необходимых для последующего вычисления строки при передаче результата клиенту.
  2. Для справочника платформа формирует поля идентификатора и наименования (или кода): _IDRRef и _Description.
  3. Для документа генерируется иной состав колонок: _IDRRef, _Number и _Date_Time.
  4. Для составного ссылочного типа создается расширенный набор служебных полей под все возможные типы метаданных.

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

Когда итоговый результат возвращается платформе 1С, она пытается сформировать строковое представление из полученных колонок. Обнаружив в наборе служебных полей значения NULL, механизм платформы возвращает результирующий NULL вместо ожидаемой строки.

Стандарты платформы по использованию представлений

Согласно стандартам разработки фирмы «1С», поля представлений (как поле Представление, так и функции ПРЕДСТАВЛЕНИЕ() / ПРЕДСТАВЛЕНИЕССЫЛКИ()) предназначены исключительно для отображения данных пользователю в самом внешнем запросе. Их запрещено использовать:

  1. В запросах, результат которых помещается во временную таблицу (секция ПОМЕСТИТЬ).
  2. Во вложенных подзапросах.
  3. В условиях и группировках: секциях ГДЕ, ИМЕЮЩИЕ, ПО, ВЫБОР, СГРУППИРОВАТЬ ПО, УПОРЯДОЧИТЬ ПО.

Игнорирование этих правил ведет к неопределенному поведению запросов, некорректным соединениям и неожиданным значениям NULL, особенно при выполнении процедур через внешние COM-соединения, где отсутствуют интерактивные клиентские механизмы кэширования представлений.

Решение 1: Хранение ссылок во временных таблицах (рекомендуемый подход)

Наиболее правильный и производительный способ — помещать во временные таблицы исходные ссылки объектов, а функцию ПРЕДСТАВЛЕНИЕ() вызывать только в финальном запросе, который передает данные вызывающему коду.

Разберем пример реализации по шагам:


// 1. Формируем первую временную таблицу с исходной ссылкой
ВЫБРАТЬ
    АвансовыйОтчет.Ссылка КАК ДокументСсылка,
    АвансовыйОтчет.СуммаДокумента КАК Сумма
ПОМЕСТИТЬ ВТ_Первая
ИЗ
    Документ.АвансовыйОтчет КАК АвансовыйОтчет
;

// 2. Формируем вторую временную таблицу с исходной ссылкой
ВЫБРАТЬ
    ВозвратТоваров.Ссылка КАК ДокументСсылка,
    ВозвратТоваров.СуммаДокумента КАК Сумма
ПОМЕСТИТЬ ВТ_Вторая
ИЗ
    Документ.ВозвратТоваровПоставщику КАК ВозвратТоваров
;

// 3. Объединяем таблицы по ссылкам, а ПРЕДСТАВЛЕНИЕ получаем только на выходе
ВЫБРАТЬ
    ПРЕДСТАВЛЕНИЕ(ВТ.ДокументСсылка) КАК ДокументПредставление,
    ВТ.Сумма КАК Сумма
ИЗ
    (ВЫБРАТЬ
        ВТ_Первая.ДокументСсылка КАК ДокументСсылка,
        ВТ_Первая.Сумма КАК Сумма
    ИЗ
        ВТ_Первая КАК ВТ_Первая

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

    ВЫБРАТЬ
        ВТ_Вторая.ДокументСсылка КАК ДокументСсылка,
        ВТ_Вторая.Сумма КАК Сумма
    ИЗ
        ВТ_Вторая КАК ВТ_Вторая) КАК ВТ

В этом случае платформа объединяет ссылки единого базового типа, а строковые представления строит централизованно один раз на этапе выгрузки выборки.

Решение 2: Явное приведение реквизитов к строке в запросе

Если логика алгоритма категорически требует наличия именно строковых полей уже внутри промежуточных временных таблиц, не используйте виртуальные функции представлений. Выбирайте конкретные реквизиты объектов и явно приводите их к строке фиксированной длины с помощью функции ВЫРАЗИТЬ().

Посмотрим на пример формирования строки для документов и справочников:


// Для справочника берем явное строковое наименование
ВЫБРАТЬ
    ВЫРАЗИТЬ(Контрагенты.Наименование КАК СТРОКА(150)) КАК ПредставлениеОбъекта
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    Справочник.Контрагенты КАК Контрагенты

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


ВЫБРАТЬ
    ВЫРАЗИТЬ("Авансовый отчет № " + Документ.Номер КАК СТРОКА(100)) КАК ДокументСтрокой
ПОМЕСТИТЬ ВТ_Документы
ИЗ
    Документ.АвансовыйОтчет КАК Документ

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

Решение 3: Предварительное получение представлений через ТаблицуЗначений

Когда требуется сложное системное представление (с учетом префиксов, номеров и логики событий модулей менеджеров ОбработкаПолученияПредставления), а получить его в запросе без ссылок невозможно, разделим процесс на два этапа:

  1. Выгрузим результат исходного запроса в объект ТаблицаЗначений.
  2. Обойдем строки и заполним строковую колонку штатной функцией Строка(СтрокаТЗ.Ссылка).
  3. Загрузим полученную таблицу значений обратно в менеджер временных таблиц в виде типизированной таблицы.

Проанализируем пример кода на встроенном языке 1С:


// Получаем данные с реальными ссылками
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    Документ.Ссылка КАК СсылкаНаДокумент,
|    Документ.СуммаДокумента КАК Сумма
|ИЗ
|    Документ.АвансовыйОтчет КАК Документ";

ТаблицаДанных = Запрос.Выполнить().Выгрузить();

// Добавляем строго типизированную строковую колонку
ТаблицаДанных.Колонки.Добавить("СсылкаСтрокой", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));

Для Каждого Стр Из ТаблицаДанных Цикл
    Стр.СсылкаСтрокой = Строка(Стр.СсылкаНаДокумент);
КонецЦикла;

// Передаем таблицу значений обратно в запрос через временную таблицу
ЗапросИТД = Новый Запрос;
ЗапросИТД.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
ЗапросИТД.Параметры.Вставить("ТаблицаДанных", ТаблицаДанных);

ЗапросИТД.Текст = 
"ВЫБРАТЬ
|    ТЗ.СсылкаСтрокой КАК ПредставлениеДокумента,
|    ТЗ.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТ_ГотовыеДанные
|ИЗ
|    &ТаблицаДанных КАК ТЗ";
ЗапросИТД.Выполнить();

Этот способ исключает зависимость от специфики разворачивания виртуальных полей платформы в СУБД и надежно работает при любых вариантах вызова, включая COM-соединения и фоновые задания.

← На главную