Каждый разработчик 1С рано или поздно сталкивается с задачей, когда в запросе необходимо отфильтровать данные по конкретному элементу справочника, который при этом не является предопределенным. Прямое сравнение реквизитов (например, по коду или наименованию) прямо в тексте запроса часто приводит к тому, что запрос возвращает пустой результат. Давайте разберем, почему так происходит, как правильно диагностировать эту проблему и какие архитектурные подходы существуют для работы с непредопределенными данными в 1С.
Рассмотрим ситуацию из практики: вам нужно выбрать родственников со степенью родства «Сын» (код 022) или «Дочь» (код 023). Вы пишете следующее условие в запросе:
ФизическиеЛицаСоставСемьи.СтепеньРодства.Код = "023"
ИЛИ ФизическиеЛицаСоставСемьи.СтепеньРодства.Код = "022"
Однако запрос упорно не возвращает строки. Выясним причину этой проблемы. Существует две наиболее частые технические причины, по которым СУБД считает условия ложными.
1. Несовпадение типов данных
В платформе 1С код справочника может иметь тип Число или Строка. Если в метаданных справочника установлен тип кода Число, то при записи кода значение «022» автоматически превращается в число 22. В запросе же вы сравниваете числовое поле со строкой "022". База данных сравнивает разные типы данных и всегда возвращает ложь.
Решение: если тип кода числовой, условие должно выглядеть так:
ФизическиеЛицаСоставСемьи.СтепеньРодства.Код = 22
ИЛИ ФизическиеЛицаСоставСемьи.СтепеньРодства.Код = 23
2. Лидирующие нули, пробелы и фиксированная длина строки
Если тип кода — Строка, обратите внимание на длину кода, заданную в конфигураторе. Например, если длина кода составляет 9 символов, а вы записали строку "022", платформа могла автоматически дополнить строку пробелами справа до полной длины. В базе данных значение будет храниться как "022 ". Строка "022" и строка "022 " для СУБД — это абсолютно разные значения.
Также возможна ситуация, когда код автоматически дополняется лидирующими нулями слева (например, "000000022"), из-за чего прямое равенство с короткой строкой также не сработает.
Чтобы понять, почему отбор не работает, выполните простую диагностику:
ФизическиеЛицаСоставСемьи.СтепеньРодства.Код и саму ссылку ФизическиеЛицаСоставСемьи.СтепеньРодства.В крайнем случае (хотя это крайне не рекомендуется из-за падения производительности), можно применить оператор ПОДОБНО для поиска подстроки:
ФизическиеЛицаСоставСемьи.СтепеньРодства.Код ПОДОБНО "%022%"
Прописывать конкретные коды или наименования («хардкодить») непосредственно в тексте запроса — это грубая ошибка проектирования. Если конечный пользователь в режиме «1С:Предприятие» изменит код элемента справочника или переименует его, ваш алгоритм перестанет работать.
Для решения этой задачи существуют три надежных и профессиональных способа.
Самый правильный и гибкий подход — найти ссылки на нужные элементы во встроенном языке (через менеджер справочника) и передать полученные ссылки в запрос в качестве параметров. При поиске по коду во встроенном языке платформа сама корректно обработает длину строки и типы данных.
Рассмотрим пример кода:
// 1. Находим ссылки во встроенном языке по коду
СсылкаСын = Справочники.СтепениРодства.НайтиПоКоду("022");
СсылкаДочь = Справочники.СтепениРодства.НайтиПоКоду("023");
МассивСтепеней = Новый Массив;
Если ЗначениеЗаполнено(СсылкаСын) Тогда
МассивСтепеней.Добавить(СсылкаСын);
КонецЕсли;
Если ЗначениеЗаполнено(СсылкаДочь) Тогда
МассивСтепеней.Добавить(СсылкаДочь);
КонецЕсли;
// 2. Передаем подготовленный массив в параметры запроса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФизическиеЛицаСоставСемьи.Ссылка КАК ФизЛицо
|ИЗ
| Справочник.ФизическиеЛица.СоставСемьи КАК ФизическиеЛицаСоставСемьи
|ГДЕ
| ФизическиеЛицаСоставСемьи.СтепеньРодства В (&МассивСтепеней)";
Запрос.УстановитьПараметр("МассивСтепеней", МассивСтепеней);
РезультатЗапроса = Запрос.Выполнить();
Если код справочника пользователи могут случайно отредактировать, поиск по коду тоже может дать сбой. Самый жесткий способ привязаться к непредопределенному элементу — использовать его уникальный идентификатор (GUID/UUID). Идентификатор присваивается элементу один раз при создании в базе данных и никогда не изменяется.
Посмотрим на пример реализации:
// Получаем уникальный идентификатор элемента (его можно узнать программно или из ссылки)
УИД_Сын = Новый УникальныйИдентификатор("f48d5162-81ab-11e8-80f1-00155d012402");
СсылкаСын = Справочники.СтепениРодства.ПолучитьСсылка(УИД_Сын);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФизическиеЛицаСоставСемьи.Ссылка КАК ФизЛицо
|ИЗ
| Справочник.ФизическиеЛица.СоставСемьи КАК ФизическиеЛицаСоставСемьи
|ГДЕ
| ФизическиеЛицаСоставСемьи.СтепеньРодства = &СсылкаСын";
Запрос.УстановитьПараметр("СсылкаСын", СсылкаСын);
РезультатЗапроса = Запрос.Выполнить();
Если эти элементы критически важны для бизнес-логики программы, лучшим решением будет сделать их предопределенными в конфигураторе. Однако, если элементы уже давно созданы пользователями в рабочей базе как обычные данные, их можно связать с новыми предопределенными элементами конфигуратора через стандартный реквизит ИмяПредопределенныхДанных.
Проанализируем этот процесс по шагам:
СтепениРодства создаем новые предопределенные элементы с именами Сын и Дочь.
ОбъектСвязи = СсылкаНаСуществующегоСына.ПолучитьОбъект();
ОбъектСвязи.ИмяПредопределенныхДанных = "Сын";
ОбъектСвязи.Записать();
После этой операции элементы в пользовательском режиме получат специальную пиктограмму (желтый кружок с синей точкой), обозначающую предопределенный элемент. Теперь вы можете использовать классическую конструкцию языка запросов ЗНАЧЕНИЕ():
ВЫБРАТЬ
ФизическиеЛицаСоставСемьи.Ссылка КАК ФизЛицо
ИЗ
Справочник.ФизическиеЛица.СоставСемьи КАК ФизическиеЛицаСоставСемьи
ГДЕ
ФизическиеЛицаСоставСемьи.СтепеньРодства = ЗНАЧЕНИЕ(Справочник.СтепениРодства.Сын)
ИЛИ ФизическиеЛицаСоставСемьи.СтепеньРодства = ЗНАЧЕНИЕ(Справочник.СтепениРодства.Дочь)
Этот способ является наиболее красивым и архитектурно правильным с точки зрения платформы 1С 8.3, так как он полностью исключает необходимость поиска элементов по кодам или GUID во встроенном языке перед выполнением запроса.