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