Как в запросе 1С преобразовать перечисление в строку и найти значение по его представлению?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С часто возникают две связанные задачи: как в запросе вывести понятное пользователю текстовое представление значения перечисления (например, "НДС 20%" вместо системной ссылки) и как выполнить обратную операцию — найти в базе данных записи, соответствующие определенной строке (например, найти все документы со ставкой НДС "20%"). Рассмотрим подробно несколько способов решения этих задач, их преимущества и недостатки. Для этой задачи есть консоль запросов и кода разработчика.

Решение 1: Получение строкового представления с помощью функции ПРЕДСТАВЛЕНИЕ()

Самый простой и прямой способ получить в запросе пользовательское представление (синоним) значения перечисления — это использовать функцию ПРЕДСТАВЛЕНИЕ(). Она преобразует ссылочное значение в его строковый эквивалент, заданный в конфигураторе.

Посмотрим на пример. Чтобы получить список всех значений перечисления СтавкиНДС и их синонимы, можно использовать следующий запрос:


ВЫБРАТЬ
    СтавкиНДС.Ссылка КАК СсылкаНаПеречисление,
    ПРЕДСТАВЛЕНИЕ(СтавкиНДС.Ссылка) КАК ПредставлениеНДС
ИЗ
    Перечисление.СтавкиНДС КАК СтавкиНДС

Результатом будет таблица с двумя колонками: одна с системной ссылкой, другая — с понятным текстом ("Без НДС", "НДС 0%", "НДС 10%", "НДС 20%" и т.д.). Этот метод идеально подходит для вывода данных в отчеты и динамические списки.

Важный момент: Использование функции ПРЕДСТАВЛЕНИЕ() в условии ГДЕ крайне не рекомендуется. Например, такой запрос будет работать очень медленно на больших объемах данных:


// Пример НЕЭФФЕКТИВНОГО запроса
ВЫБРАТЬ
    ДокументПродажи.Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК ДокументПродажи
ГДЕ
    ПРЕДСТАВЛЕНИЕ(ДокументПродажи.СтавкаНДС) = &НДССтрокой

Причина низкой производительности в том, что ПРЕДСТАВЛЕНИЕ() — это функция языка 1С, а не SQL. Система сначала получит из базы данных все записи, и только потом, на уровне сервера 1С, будет применять фильтр к текстовому представлению. Это лишает СУБД возможности использовать индексы для быстрого поиска.

Решение 2: Поиск перечисления по строке. Ручное сопоставление через ВЫБОР КОГДА

Если вам нужно в самом запросе сопоставить значения перечисления с определенными строками, можно использовать конструкцию ВЫБОР КОГДА ... ТОГДА ... КОНЕЦ. Этот подход позволяет "захардкодить" логику прямо в тексте запроса.

Этот метод может быть полезен, когда нужно не просто получить представление, а, например, сгруппировать значения или присвоить им какой-то признак.


ВЫБРАТЬ
    СтавкиНДС.Ссылка,
    ВЫБОР
        КОГДА СтавкиНДС.Ссылка = ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС20)
            ТОГДА "Ставка 20 процентов"
        КОГДА СтавкиНДС.Ссылка = ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС10)
            ТОГДА "Ставка 10 процентов"
        КОГДА СтавкиНДС.Ссылка = ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.БезНДС)
            ТОГДА "Без налога"
        ИНАЧЕ "Другая ставка"
    КОНЕЦ КАК ОписаниеСтавки
ИЗ
    Перечисление.СтавкиНДС КАК СтавкиНДС

Преимущества:

Недостатки:

Решение 3: Правильный поиск по строке — подготовка данных до запроса

Как мы выяснили, фильтровать по представлению в запросе неэффективно. Правильный подход — найти нужную ссылку на перечисление средствами языка 1С до выполнения запроса и передать ее в качестве параметра.

Для этого можно создать вспомогательную функцию (поможет отладка кода в режиме Предприятие), которая будет искать значение перечисления по его синониму или имени, перебирая метаданные.

Пример поиска по синониму:


&НаСервере
Функция НайтиСтавкуНДСПоПредставлению(ИскомоеПредставление)
    
    Для Каждого ЗначениеПеречисления Из Метаданные.Перечисления.СтавкиНДС.Значения() Цикл
        // Сравниваем синоним из метаданных с искомой строкой
        Если НРег(ЗначениеПеречисления.Синоним) = НРег(ИскомоеПредставление) Тогда
            // Возвращаем ссылку на найденное значение
            Возврат Перечисления.СтавкиНДС[ЗначениеПеречисления.Имя];
        КонецЕсли;
    КонецЦикла;
    
    // Если ничего не найдено
    Возврат Перечисления.СтавкиНДС.ПустаяСсылка();
    
КонецФункции

// Пример использования
&НаКлиенте
Процедура ВыполнитьЗапрос()

    СтрокаНДС = "20%"; // Строка, по которой ищем
    НайденнаяСтавка = НайтиСтавкуНДСПоПредставлению(СтрокаНДС);

    Если НЕ НайденнаяСтавка.Пустая() Тогда
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |    РеализацияТоваровУслуг.Ссылка
            |ИЗ
            |    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
            |ГДЕ
            |    РеализацияТоваровУслуг.СтавкаНДС = &СтавкаНДС";
        
        Запрос.УстановитьПараметр("СтавкаНДС", НайденнаяСтавка);
        Результат = Запрос.Выполнить().Выгрузить();
        // Дальнейшая обработка результата
    КонецЕсли;

КонецПроцедуры

Этот подход является наиболее производительным и правильным с точки зрения архитектуры, так как позволяет СУБД использовать индекс по полю перечисления для максимально быстрого отбора данных.

Решение 4: Использование временных таблиц для сложного сопоставления

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

Разберем по шагам:

  1. Создаем таблицу значений. В коде 1С создается таблица с двумя колонками, например, "НДССтрокой" и "СсылкаНаПеречисление".
  2. Заполняем таблицу. Программно перебираем значения перечисления и заполняем таблицу соответствий.
  3. Передаем в запрос. Создаем менеджер временных таблиц и помещаем в него нашу таблицу.
  4. Соединяем в запросе. В основном запросе используем ЛЕВОЕ СОЕДИНЕНИЕ с временной таблицей по строковому полю.

Посмотрим на пример:


// 1, 2. Создаем и заполняем таблицу значений
ТаблицаСоответствия = Новый ТаблицаЗначений;
ТаблицаСоответствия.Колонки.Добавить("НДССтрокой", Новый ОписаниеТипов("Строка"));
ТаблицаСоответствия.Колонки.Добавить("СсылкаНаПеречисление", Новый ОписаниеТипов("ПеречислениеСсылка.СтавкиНДС"));

НоваяСтрока = ТаблицаСоответствия.Добавить();
НоваяСтрока.НДССтрокой = "20%";
НоваяСтрока.СсылкаНаПеречисление = Перечисления.СтавкиНДС.НДС20;

НоваяСтрока = ТаблицаСоответствия.Добавить();
НоваяСтрока.НДССтрокой = "Без налога";
НоваяСтрока.СсылкаНаПеречисление = Перечисления.СтавкиНДС.БезНДС;

// 3. Передаем в запрос
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

Запрос.Текст = "ВЫБРАТЬ
|    ТЗ.НДССтрокой,
|    ТЗ.СсылкаНаПеречисление
|ПОМЕСТИТЬ ВТ_СоответствиеНДС
|ИЗ
|    &ТаблицаСоответствия КАК ТЗ;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    Продажи.Номенклатура,
|    Продажи.Сумма,
|    ВТ_СоответствиеНДС.СсылкаНаПеречисление
|ИЗ
|    РегистрНакопления.Продажи КАК Продажи
|        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СоответствиеНДС
|        ПО Продажи.ПредставлениеСтавкиНДС = ВТ_СоответствиеНДС.НДССтрокой"; // Предполагаем, что в регистре есть строковое поле

Запрос.УстановитьПараметр("ТаблицаСоответствия", ТаблицаСоответствия);
Результат = Запрос.Выполнить();

Этот метод очень гибок, так как вся логика сопоставления вынесена в код и не требует изменения текста запроса.

Рекомендации для обмена данными

Если задача стоит в контексте обмена данными между разными базами (например, УТ и БП), то полагаться на синонимы (ПРЕДСТАВЛЕНИЕ()) категорически нельзя. Синоним может быть изменен пользователем, он зависит от языка конфигурации и не является надежным идентификатором.

Для надежного обмена данными следует использовать инвариантные (неизменяемые) свойства:

  1. Имя значения перечисления. Это самый надежный способ. Имя задается в конфигураторе и не может быть изменено пользователем.
    • Получить имя: Строка(Перечисления.СтавкиНДС.НДС20) вернет "НДС20".
    • Найти по имени: Перечисления.СтавкиНДС["НДС20"] вернет ссылку на нужное значение.
  2. Порядок (индекс) значения. Можно получить порядок значения функцией Порядок(). Этот способ тоже рабочий, но менее надежен, чем имя, так как теоретически порядок значений в перечислении может измениться при обновлении конфигурации, хотя на практике это случается редко.

Вывод: для надежного обмена данными всегда передавайте имя значения перечисления, а не его синоним или строковое представление.

← На главную