Почему конструкция ВЫРАЗИТЬ для поля составного типа возвращает NULL и как получить реквизиты справочника?

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

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

Разберем детально, почему так происходит, как правильно диагностировать тип хранящихся данных и какими способами можно получить необходимые значения в запросе.

Анализ проблемы: почему не работает ВЫРАЗИТЬ

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


ВЫБРАТЬ
    РеестрОтпусков.Сотрудник КАК Сотрудник,
    ВЫБОР
        КОГДА ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Справочник.ВидыОтпусков).НаименованиеПолное = ""
            ТОГДА ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Справочник.ВидыОтпусков).Наименование
        ИНАЧЕ ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Справочник.ВидыОтпусков).НаименованиеПолное
    КОНЕЦ КАК ВидОтпуска
ИЗ
    РегистрСведений.РеестрОтпусков КАК РеестрОтпусков

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

В языке запросов 1С проверка NULL = "" всегда дает результат ЛОЖЬ. В итоге ветка ТОГДА не отрабатывает, выполнение уходит в секцию ИНАЧЕ, которая опять возвращает NULL, и колонка в отчете остается пустой.

Шаг 1. Диагностика фактического типа данных

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


ВЫБРАТЬ
    РеестрОтпусков.ВидОтпуска,
    ТИПЗНАЧЕНИЯ(РеестрОтпусков.ВидОтпуска) КАК ФактическийТип,
    РеестрОтпусков.ВидОтпуска ССЫЛКА Справочник.ВидыОтпусков КАК ЭтоСсылка
ИЗ
    РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
ГДЕ
    РеестрОтпусков.Сотрудник = &Сотрудник

Если колонка ФактическийТип возвращает Строка, а колонка ЭтоСсылка — значение ЛОЖЬ, значит, в базу данных была записана текстовая строка, а не объектная ссылка. У строки нет реквизитов Наименование или НаименованиеПолное, поэтому стандартное разыменование через точку принципиально невозможно.

Решение 1. Соединение со справочником по строковому наименованию

Если в регистре уже зафиксированы текстовые строки и изменить логику заполнения данных на текущем этапе нельзя, свяжем строку из регистра с наименованием элемента справочника через явное ЛЕВОЕ СОЕДИНЕНИЕ:


ВЫБРАТЬ
    РеестрОтпусков.Сотрудник КАК Сотрудник,
    ЕСТЬNULL(СпрВидыОтпусков.НаименованиеПолное, РеестрОтпусков.ВидОтпуска) КАК ВидОтпуска,
    РеестрОтпусков.НачалоПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодС,
    РеестрОтпусков.КонецПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодПо
ИЗ
    РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВидыОтпусков КАК СпрВидыОтпусков
        ПО РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Наименование
ГДЕ
    РеестрОтпусков.Сотрудник = &Сотрудник

Проанализируем работу этого запроса:

  1. Конструкция ПО РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Наименование сопоставляет строковое поле регистра со стандартным реквизитом справочника Наименование.
  2. Функция ЕСТЬNULL(...) выводит полное наименование найденного элемента, а если соответствие не обнаружено — оставляет исходную строку из регистра.

Важные ограничения соединения по строкам:

  1. Дублирование строк: Реквизит Наименование в справочниках 1С не уникален. Если в справочнике присутствуют дубли или помеченные на удаление элементы с таким же названием, левое соединение размножит строки отчета.
  2. Строки неограниченной длины: Если ресурс регистра имеет неограниченную длину, СУБД вернет ошибку при сравнении. В этом случае строку необходимо явно ограничить: ПО ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Строка(150)) = СпрВидыОтпусков.Наименование.
  3. Производительность: Сравнение строковых полей (NVARCHAR) в СУБД работает значительно медленнее, чем соединение по бинарным идентификаторам ссылок (GUID), и может приводить к полному сканированию таблиц.

Решение 2. Корректная обработка смешанных типов (строка + ссылка)

Если в регистре могут одновременно присутствовать как ссылки, так и текстовые наименования, настроим универсальное соединение, обрабатывающее оба варианта:


ВЫБРАТЬ
    РеестрОтпусков.Сотрудник КАК Сотрудник,
    ВЫБОР
        КОГДА СпрВидыОтпусков.Ссылка ЕСТЬ NULL ТОГДА РеестрОтпусков.ВидОтпуска
        КОГДА СпрВидыОтпусков.НаименованиеПолное <> "" ТОГДА СпрВидыОтпусков.НаименованиеПолное
        ИНАЧЕ СпрВидыОтпусков.Наименование
    КОНЕЦ КАК ВидОтпуска,
    РеестрОтпусков.НачалоПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодС,
    РеестрОтпусков.КонецПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодПо
ИЗ
    РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВидыОтпусков КАК СпрВидыОтпусков
        ПО РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Ссылка
        ИЛИ РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Наименование

Решение 3. Архитектурное исправление (нормализация данных)

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

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

ВЫБРАТЬ
    РеестрОтпусков.Сотрудник КАК Сотрудник,
    ВЫБОР
        КОГДА РеестрОтпусков.ВидОтпуска.НаименованиеПолное <> ""
            ТОГДА РеестрОтпусков.ВидОтпуска.НаименованиеПолное
        ИНАЧЕ РеестрОтпусков.ВидОтпуска.Наименование
    КОНЕЦ КАК ВидОтпуска,
    РеестрОтпусков.НачалоПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодС,
    РеестрОтпусков.КонецПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодПо
ИЗ
    РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
← На главную