Как в запросе 1С правильно сравнить элемент справочника и текстовое наименование характеристики?

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Промышленность, строительство и АПК
← На главную

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

Суть исходной проблемы и разбор запроса

Посмотрим на пример из реальной практики. Разработчик написал функцию для расчета суммарной некондиции (брака) по конкретному производственному цеху. В функцию передаются параметры: дата, смена и сам цех (в виде ссылки на справочник подразделений или складов). Задача — отобрать из табличной части документа ОтчетПроизводстваЗаСмену строки, где наименование характеристики номенклатуры идентично названию переданного цеха.

Рассмотрим фрагмент исходного кода с запросом:


Функция ВычислитьСуммарнуюНекондициюЦех(Дт, Смена, Цех)
    Брак = Справочники.Номенклатура.НайтиПоНаименованию("Брак");
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    СУММА(ОтчетПроизводстваЗаСменуПродукция.Количество * ОтчетПроизводстваЗаСменуПродукция.Коэффициент) КАК СуммарныеПотери
    |ИЗ
    |    Документ.ОтчетПроизводстваЗаСмену.Продукция КАК ОтчетПроизводстваЗаСменуПродукция
    |ГДЕ
    |    ОтчетПроизводстваЗаСменуПродукция.Ссылка.ОтражатьВУправленческомУчете = ИСТИНА
    |    И ОтчетПроизводстваЗаСменуПродукция.Номенклатура В ИЕРАРХИИ(&Брак)
    |    И ОтчетПроизводстваЗаСменуПродукция.Ссылка.Дата МЕЖДУ &Дт1 И &Дт2
    |    И ОтчетПроизводстваЗаСменуПродукция.Ссылка.Смена = &См
    |    И ОтчетПроизводстваЗаСменуПродукция.ХарактеристикаНоменклатуры.Наименование = &Ц";

    Запрос.УстановитьПараметр("Дт1", НачалоДня(Дт));
    Запрос.УстановитьПараметр("Дт2", КонецДня(Дт));
    Запрос.УстановитьПараметр("Брак", Брак);
    Запрос.УстановитьПараметр("См", Смена);
    Запрос.УстановитьПараметр("Ц", Цех);

    Выборка = Запрос.Выполнить().Выбрать();
    // Дальнейшая обработка...
КонецФункции

При выполнении этого кода разработчик получал пустой результат. Выясним причину этой ошибки. Проблема кроется в последней строке передачи параметров: Запрос.УстановитьПараметр("Ц", Цех);. В параметре запроса ожидается строка, так как мы сравниваем его с полем Наименование справочника характеристик. Однако переменная Цех содержит объект типа СправочникСсылка. Система 1С и СУБД (например, MS SQL Server) не могут автоматически приравнять ссылочный идентификатор к текстовой строке, поэтому условие сравнения всегда возвращает ложь, отсекая все строки выборки.

Правильное решение: передача строкового параметра

Чтобы запрос отработал корректно, необходимо передавать в параметр именно строку. Разберем правильный вариант инициализации параметра:


// Извлекаем текстовое наименование из ссылочного объекта
Запрос.УстановитьПараметр("Ц", Цех.Наименование);

// Либо, как альтернатива, приводим ссылку к строке явно:
Запрос.УстановитьПараметр("Ц", Строка(Цех));

После замены Цех на Цех.Наименование типы данных в условии ГДЕ ОтчетПроизводстваЗаСменуПродукция.ХарактеристикаНоменклатуры.Наименование = &Ц совпадают (Строка = Строка), и система успешно находит нужные записи. Однако на этом анализ ситуации заканчивать нельзя, так как сравнение по тексту таит в себе множество неочевидных проблем.

Скрытые символы и проблемы с кодировкой

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

  1. Смешение кириллицы и латиницы: Пользователи при вводе данных могут случайно набрать буквы «а», «о», «с», «е», «р» в английской раскладке клавиатуры. Визуально "Цех" (с русской 'е') и "Цeх" (с английской 'e') ничем не отличаются, но для базы данных это две совершенно разные строки с разными кодами символов.
  2. Неразрывные пробелы: Часто данные копируются из Excel или сторонних программ. При этом вместо обычного пробела (с кодом 32) может вставиться неразрывный пробел (с кодом 160). Запрос воспримет такие строки как разные.
  3. Пробелы в начале и конце строки: Случайно поставленный лишний пробел в конце наименования одной из характеристик разрушит равенство.

Чтобы минимизировать влияние лишних пробелов, рекомендуем применять функцию отсечения пустых символов СокрЛП() при передаче параметра:


Запрос.УстановитьПараметр("Ц", СокрЛП(Цех.Наименование));

Важное замечание про регистр: По умолчанию в большинстве клиент-серверных баз 1С (особенно на базе MS SQL Server) сравнение строк в запросах нечувствительно к регистру символов (Case Insensitive). Строки "ЦЕХ 1" и "цех 1" будут признаны равными. Но если используется PostgreSQL с определенными настройками collation, регистр может иметь решающее значение. Всегда учитывайте конфигурацию вашего сервера.

Оптимизация производительности: проблема «точек» в запросах

Проанализируем текст запроса с точки зрения производительности базы данных. В условии присутствует конструкция ОтчетПроизводстваЗаСменуПродукция.ХарактеристикаНоменклатуры.Наименование. В языке запросов 1С использование обращений через «точку» к реквизитам ссылочных полей является крайне нежелательным, особенно в секции ГДЕ.

Каждая такая «точка» транслируется СУБД в неявное левое соединение (LEFT JOIN) с таблицей справочника характеристик. Если табличная часть отчета производства содержит тысячи строк, такое соединение может серьезно замедлить выполнение функции. Кроме того, поиск по строковым полям всегда медленнее поиска по ссылкам (внутренним идентификаторам GUID).

Архитектурные рекомендации: почему связь по наименованию — это плохая практика?

Сопоставление данных через текстовые наименования считается архитектурной ошибкой в системах на платформе 1С. Рассмотрим подробнее, к чему это приводит:

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

Как сделать правильно? Введем структурные изменения в конфигурацию. Лучшим решением будет добавить в справочник ХарактеристикиНоменклатуры дополнительный реквизит (например, ПодразделениеЦех) с типом СправочникСсылка.Подразделения (или склады, в зависимости от логики).

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


Запрос.Текст = 
"ВЫБРАТЬ
|    СУММА(ТЧ_Продукция.Количество * ТЧ_Продукция.Коэффициент) КАК СуммарныеПотери
|ИЗ
|    Документ.ОтчетПроизводстваЗаСмену.Продукция КАК ТЧ_Продукция
|    // Явное внутреннее соединение с характеристиками
|    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ХарактеристикиНоменклатуры КАК Характеристики
|    ПО ТЧ_Продукция.ХарактеристикаНоменклатуры = Характеристики.Ссылка
|ГДЕ
|    ТЧ_Продукция.Ссылка.ОтражатьВУправленческомУчете = ИСТИНА
|    И ТЧ_Продукция.Номенклатура В ИЕРАРХИИ(&Брак)
|    И ТЧ_Продукция.Ссылка.Дата МЕЖДУ &Дт1 И &Дт2
|    И ТЧ_Продукция.Ссылка.Смена = &См
|    // Теперь сравниваем ссылки, а не строки!
|    И Характеристики.ПодразделениеЦех = &ЦехПараметр";

// Передаем саму ссылку, а не строку!
Запрос.УстановитьПараметр("ЦехПараметр", Цех);

Инструменты отладки сложных запросов

Автор исходной проблемы отказывался от использования встроенного отладчика (Конфигуратора), опасаясь "заморозить" работу пользователей. Если вы разрабатываете функционал на рабочей или тестовой базе без возможности запустить полноценную отладку, воспользуйтесь расширенными консолями запросов (например, из комплекта Инструментов Разработчика).

Такие консоли позволяют не только выполнить запрос, но и посмотреть реальный сгенерированный SQL-код, который платформа отправляет серверу СУБД. Также полезной техникой при написании подобных запросов является временное отключение проблемного условия ГДЕ и вывод исследуемого поля в результирующую часть (секция ВЫБРАТЬ). Так вы сможете глазами увидеть, какие именно значения лежат в базе, скопировать их и сравнить с вашим ожидаемым параметром посимвольно.

Внимательно относитесь к типам передаваемых параметров и старайтесь проектировать структуру конфигурации так, чтобы избегать строковых связей между ключевыми объектами учета.

← На главную