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