При разработке запросов в платформе 1С:Предприятие часто приходится работать с ресурсами или реквизитами составного типа, объединяющими ссылочные типы (например, СправочникСсылка.ВидыОтпусков) и примитивные типы (Строка). Распространенная ситуация: разработчик пытается извлечь дополнительный реквизит (например, НаименованиеПолное) через конструкцию ВЫРАЗИТЬ(...), однако запрос возвращает значение NULL или пустое поле, даже если в справочнике данные гарантированно заполнены.
Разберем детально, почему так происходит, как правильно диагностировать тип хранящихся данных и какими способами можно получить необходимые значения в запросе.
Рассмотрим типовой ошибочный запрос, в котором к ресурсу регистра сведений применяется явное приведение типов:
ВЫБРАТЬ
РеестрОтпусков.Сотрудник КАК Сотрудник,
ВЫБОР
КОГДА ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Справочник.ВидыОтпусков).НаименованиеПолное = ""
ТОГДА ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Справочник.ВидыОтпусков).Наименование
ИНАЧЕ ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Справочник.ВидыОтпусков).НаименованиеПолное
КОНЕЦ КАК ВидОтпуска
ИЗ
РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
Если в поле ВидОтпуска для конкретной строки базы данных записано значение типа Строка, а не ссылка на справочник, то конструкция ВЫРАЗИТЬ(Поле КАК Справочник.ВидыОтпусков) возвращает NULL. Любая попытка разыменования полей через точку от значения NULL (например, NULL.НаименованиеПолное) также приводит к результату NULL.
В языке запросов 1С проверка NULL = "" всегда дает результат ЛОЖЬ. В итоге ветка ТОГДА не отрабатывает, выполнение уходит в секцию ИНАЧЕ, которая опять возвращает NULL, и колонка в отчете остается пустой.
Выясним точный тип значения, который реально хранится в интересующей записи. Для этого выполним диагностический запрос в консоли запросов:
ВЫБРАТЬ
РеестрОтпусков.ВидОтпуска,
ТИПЗНАЧЕНИЯ(РеестрОтпусков.ВидОтпуска) КАК ФактическийТип,
РеестрОтпусков.ВидОтпуска ССЫЛКА Справочник.ВидыОтпусков КАК ЭтоСсылка
ИЗ
РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
ГДЕ
РеестрОтпусков.Сотрудник = &Сотрудник
Если колонка ФактическийТип возвращает Строка, а колонка ЭтоСсылка — значение ЛОЖЬ, значит, в базу данных была записана текстовая строка, а не объектная ссылка. У строки нет реквизитов Наименование или НаименованиеПолное, поэтому стандартное разыменование через точку принципиально невозможно.
Если в регистре уже зафиксированы текстовые строки и изменить логику заполнения данных на текущем этапе нельзя, свяжем строку из регистра с наименованием элемента справочника через явное ЛЕВОЕ СОЕДИНЕНИЕ:
ВЫБРАТЬ
РеестрОтпусков.Сотрудник КАК Сотрудник,
ЕСТЬNULL(СпрВидыОтпусков.НаименованиеПолное, РеестрОтпусков.ВидОтпуска) КАК ВидОтпуска,
РеестрОтпусков.НачалоПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодС,
РеестрОтпусков.КонецПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодПо
ИЗ
РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВидыОтпусков КАК СпрВидыОтпусков
ПО РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Наименование
ГДЕ
РеестрОтпусков.Сотрудник = &Сотрудник
Проанализируем работу этого запроса:
ПО РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Наименование сопоставляет строковое поле регистра со стандартным реквизитом справочника Наименование.ЕСТЬNULL(...) выводит полное наименование найденного элемента, а если соответствие не обнаружено — оставляет исходную строку из регистра.Важные ограничения соединения по строкам:
Наименование в справочниках 1С не уникален. Если в справочнике присутствуют дубли или помеченные на удаление элементы с таким же названием, левое соединение размножит строки отчета.ПО ВЫРАЗИТЬ(РеестрОтпусков.ВидОтпуска КАК Строка(150)) = СпрВидыОтпусков.Наименование.NVARCHAR) в СУБД работает значительно медленнее, чем соединение по бинарным идентификаторам ссылок (GUID), и может приводить к полному сканированию таблиц.Если в регистре могут одновременно присутствовать как ссылки, так и текстовые наименования, настроим универсальное соединение, обрабатывающее оба варианта:
ВЫБРАТЬ
РеестрОтпусков.Сотрудник КАК Сотрудник,
ВЫБОР
КОГДА СпрВидыОтпусков.Ссылка ЕСТЬ NULL ТОГДА РеестрОтпусков.ВидОтпуска
КОГДА СпрВидыОтпусков.НаименованиеПолное <> "" ТОГДА СпрВидыОтпусков.НаименованиеПолное
ИНАЧЕ СпрВидыОтпусков.Наименование
КОНЕЦ КАК ВидОтпуска,
РеестрОтпусков.НачалоПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодС,
РеестрОтпусков.КонецПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодПо
ИЗ
РегистрСведений.РеестрОтпусков КАК РеестрОтпусков
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВидыОтпусков КАК СпрВидыОтпусков
ПО РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Ссылка
ИЛИ РеестрОтпусков.ВидОтпуска = СпрВидыОтпусков.Наименование
Смешивание ссылочных и строковых типов в одном реквизите противоречит стандартам разработки 1С и утяжеляет структуру таблиц базы данных. Правильное долговременное решение состоит в нормализации структуры:
ВидОтпуска заполнен строкой, выполним поиск элемента методом Справочники.ВидыОтпусков.НайтиПоНаименованию() и перезапишем ресурс реальной ссылкой.ВидОтпуска и снимем флажок с типа Строка, оставив исключительно СправочникСсылка.ВидыОтпусков.
ВЫБРАТЬ
РеестрОтпусков.Сотрудник КАК Сотрудник,
ВЫБОР
КОГДА РеестрОтпусков.ВидОтпуска.НаименованиеПолное <> ""
ТОГДА РеестрОтпусков.ВидОтпуска.НаименованиеПолное
ИНАЧЕ РеестрОтпусков.ВидОтпуска.Наименование
КОНЕЦ КАК ВидОтпуска,
РеестрОтпусков.НачалоПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодС,
РеестрОтпусков.КонецПериодаЗаКоторыйПредоставляетсяОтпуск КАК РабочийГодПо
ИЗ
РегистрСведений.РеестрОтпусков КАК РеестрОтпусков