Решение задач 1С:Предприятие 8 на примерах

Строка в запросе 1С: ПОДОБНО, ПОДСТРОКА и строки неограниченной длины

ЗапросыОпубликовано

Поиск по части названия, отбор по началу ИНН, сборка описания из нескольких полей — со строками в запросах приходится работать постоянно, и у них есть свои ограничения. Статья объясняет, как записывать строковые литералы и параметры, как устроен ПОДОБНО с шаблонами и СПЕЦСИМВОЛ, как использовать ПОДСТРОКА и что делать со строками неограниченной длины. В конце — готовая функция поиска по нескольким словам и список типичных ошибок.

Задача: искать и преобразовывать текст прямо в запросе

Строки в запросах 1С нужны постоянно: поиск номенклатуры по части названия, отбор контрагентов по началу ИНН, сборка описания из нескольких полей, сравнение комментария с образцом. При этом у строк в языке запросов есть жёсткие ограничения — особенно у полей неограниченной длины, — и большинство «непонятных» ошибок связано именно с ними. Разберём всё по порядку: от литералов и параметров до шаблонов ПОДОБНО, функции ПОДСТРОКА и приведения типов через ВЫРАЗИТЬ. Общие приёмы работы с объектом Запрос описаны в статье про запросы в 1С.

Строковый литерал и параметр

В тексте запроса строковая константа записывается в двойных кавычках. Но сам текст запроса в коде — тоже строка 1С, поэтому внутренние кавычки приходится удваивать. Ошибка в количестве кавычек — самая частая причина, по которой запрос «не парсится» уже при создании.

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Контрагенты.Ссылка КАК Контрагент
|ИЗ
|    Справочник.Контрагенты КАК Контрагенты
|ГДЕ
|    Контрагенты.Наименование = &Наименование
|    ИЛИ Контрагенты.ИНН = ""7701234567""";

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

Если значение приходит из формы, файла или внешней системы, всегда передавайте его параметром, а не склеивайте с текстом запроса. Параметр не ломается от кавычек и служебных символов внутри значения, а платформа может переиспользовать план выполнения. Литерал оставляйте для неизменяемых констант вроде кодов и статусов.

ПОДОБНО: сравнение с шаблоном

Оператор ПОДОБНО сравнивает строку с шаблоном. В шаблоне работают четыре служебных конструкции: % — любая последовательность символов (в том числе пустая), _ — ровно один любой символ, [...] — один символ из перечисленных или из диапазона, [^...] — один символ, которого нет в перечислении.

ГДЕ
    Номенклатура.Наименование ПОДОБНО "Кабель%"        // начинается с «Кабель»
    ИЛИ Номенклатура.Наименование ПОДОБНО "%ВВГ%"      // содержит «ВВГ»
    ИЛИ Номенклатура.Артикул ПОДОБНО "КБ-___"          // «КБ-» и ещё три любых символа
    ИЛИ Номенклатура.Артикул ПОДОБНО "[0-9][0-9]%"     // начинается с двух цифр
    ИЛИ Номенклатура.Артикул ПОДОБНО "[^0-9]%"         // не начинается с цифры

Обычное сравнение = шаблонов не понимает: условие Наименование = "Кабель%" ищет строку, буквально заканчивающуюся знаком процента. Шаблон можно передать и параметром, тогда процент добавляют в коде: "%" + СтрокаПоиска + "%".

Учтите производительность. Шаблон вида "Кабель%" (поиск по началу) СУБД может выполнять по индексу, а "%кабель%" заставляет просматривать всю таблицу. На небольших справочниках это незаметно, на миллионах строк — заметно, поэтому для массового поиска используйте полнотекстовый поиск или ограничивайте выборку другими условиями.

СПЕЦСИМВОЛ: поиск знаков % и _ как обычного текста

Если искомый текст сам содержит процент, подчёркивание или квадратную скобку, их нужно «экранировать». Для этого после шаблона указывается ключевое слово СПЕЦСИМВОЛ и символ-экранировщик, а в шаблоне этот символ ставится перед служебным знаком.

ВЫБРАТЬ
    Скидки.Ссылка КАК Ссылка
ИЗ
    Справочник.ВидыСкидок КАК Скидки
ГДЕ
    Скидки.Наименование ПОДОБНО "%50\%%" СПЕЦСИМВОЛ "\"

Этот запрос найдёт все названия, где встречается «50%». Когда строка поиска вводится пользователем, экранирование лучше вынести в отдельную функцию, иначе символ подчёркивания в артикуле превратится в «любой символ», а скобка вызовет ошибку шаблона.

Функция ЭкранироватьШаблон(Знач Строка) Экспорт

    Результат = СтрЗаменить(Строка, "\", "\\");
    Результат = СтрЗаменить(Результат, "%", "\%");
    Результат = СтрЗаменить(Результат, "_", "\_");
    Результат = СтрЗаменить(Результат, "[", "\[");
    Результат = СтрЗаменить(Результат, "]", "\]");

    Возврат Результат;

КонецФункции

Поиск по нескольким словам

Типовая задача — строка поиска «кабель ввг 3х2.5», где слова могут стоять в любом порядке. Решение: разбить строку на слова и на каждое слово добавить своё условие ПОДОБНО с отдельным параметром.

&НаСервере
Функция НайтиНоменклатуру(СтрокаПоиска)

    Слова = СтрРазделить(СтрокаПоиска, " ", Ложь);
    Если Слова.Количество() = 0 Тогда
        Возврат Новый Массив;
    КонецЕсли;

    Запрос = Новый Запрос;
    Условия = Новый Массив;

    Для Индекс = 0 По Слова.ВГраница() Цикл
        ИмяПараметра = "Слово" + XMLСтрока(Индекс);
        Условия.Добавить("Номенклатура.Наименование ПОДОБНО &" + ИмяПараметра + " СПЕЦСИМВОЛ ""\""");
        Запрос.УстановитьПараметр(ИмяПараметра, "%" + ЭкранироватьШаблон(Слова[Индекс]) + "%");
    КонецЦикла;

    Запрос.Текст =
    "ВЫБРАТЬ ПЕРВЫЕ 50
    |    Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    " + СтрСоединить(Условия, " И ");

    Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

КонецФункции

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

ПОДСТРОКА: часть строки в запросе

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

ВЫБРАТЬ
    ПОДСТРОКА(Контрагенты.Наименование, 1, 1) КАК Буква,
    КОЛИЧЕСТВО(*) КАК Количество
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    ПОДСТРОКА(Контрагенты.ИНН, 1, 2) = "77"
СГРУППИРОВАТЬ ПО
    ПОДСТРОКА(Контрагенты.Наименование, 1, 1)
УПОРЯДОЧИТЬ ПО
    Буква

Условие по ПОДСТРОКА применяется к каждой строке таблицы и не использует индекс по полю. Для поиска по началу строки проще и быстрее ПОДОБНО "77%".

Строки неограниченной длины: ВЫРАЗИТЬ и ПОДСТРОКА

Реквизиты со строкой неограниченной длины — чаще всего «Комментарий» у документов и произвольные текстовые поля — хранятся в СУБД особым образом, и к ним применяются ограничения. Такие поля нельзя напрямую сравнивать операторами = и <>, группировать, использовать в упорядочивании, в РАЗЛИЧНЫЕ и в условиях соединения. Платформа сообщит об этом ошибкой при выполнении запроса. Поиск по подстроке при этом работает через ПОДОБНО.

Чтобы сравнить такое поле или сгруппировать по нему, его приводят к строке ограниченной длины. Допустимая длина ограничена 1024 символами.

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

// Альтернатива — вырезать начало строки
//    ПОДСТРОКА(Реализация.Комментарий, 1, 200) = &Комментарий

Обратите внимание: приведение обрезает значение до указанной длины. Если в комментарии больше 200 символов, сравнение пройдёт только по первым двухстам. Для группировки приведённое выражение указывают и в списке полей, и в СГРУППИРОВАТЬ ПО.

ВЫБРАТЬ
    ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(100)) КАК Комментарий,
    КОЛИЧЕСТВО(*) КАК Количество
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
СГРУППИРОВАТЬ ПО
    ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(100))

Конкатенация и число в строку

Строки складываются знаком +. Складывать можно только строки: число и дату сначала приводят к строке через ВЫРАЗИТЬ(... КАК СТРОКА(N)), ссылку — через ПРЕДСТАВЛЕНИЕ(). Длина должна быть достаточной для значения, поэтому для чисел берут запас.

ВЫБРАТЬ
    Реализация.Номер + " на сумму "
        + ВЫРАЗИТЬ(Реализация.СуммаДокумента КАК СТРОКА(20)) + " руб." КАК Описание,
    ПРЕДСТАВЛЕНИЕ(Реализация.Контрагент) КАК КонтрагентПредставление
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация

Важная особенность: если хотя бы одно слагаемое равно NULL (например, поле присоединено левым соединением и записи не нашлось), вся сумма станет NULL. Оборачивайте такие поля в ЕСТЬNULL(Поле, ""). Форматирование чисел с разделителями разрядов в запросе не делается — для этого есть функция Формат() встроенного языка или оформление в СКД.

Новые строковые функции языка запросов

Долгое время в языке запросов было всего две строковые функции — ПОДСТРОКА и приведение через ВЫРАЗИТЬ; всё остальное делали в коде или обходили шаблонами. В новых версиях платформы 8.3 язык запросов пополнился знакомыми функциями встроенного языка: длина строки (СТРДЛИНА), удаление пробелов (СОКРЛП), смена регистра (ВРЕГ, НРЕГ), поиск и замена подстроки (СТРНАЙТИ, СТРЗАМЕНИТЬ), выбор левой и правой части (ЛЕВ, ПРАВ). Точный набор и сигнатуры зависят от релиза, поэтому проверьте их в синтакс-помощнике своей версии. Если в вашей платформе они есть, запрос становится короче:

ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    СТРДЛИНА(Номенклатура.Наименование) КАК Длина,
    СОКРЛП(Номенклатура.Артикул) КАК Артикул
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    СТРНАЙТИ(ВРЕГ(Номенклатура.Наименование), ВРЕГ(&Слово)) > 0

В конфигурациях на более старых релизах те же задачи решают через ПОДСТРОКА, ПОДОБНО и постобработку результата в коде.

Регистр букв при поиске

В стандартных настройках информационной базы сравнение строк в запросе не различает регистр: "кабель", "Кабель" и "КАБЕЛЬ" считаются равными, а ПОДОБНО находит слово в любом регистре. Поэтому приводить обе стороны к одному регистру обычно не требуется. Если поведение критично (например, в базе нестандартная настройка СУБД), нормализуйте и поле, и значение через ВРЕГ, а параметр для надёжности приведите в верхний регистр функцией ВРег() встроенного языка.

Типичные ошибки со строками

  • Не удвоены кавычки внутри текста запроса — запрос не разбирается или обрезается раньше времени.
  • Сравнение или группировка по полю неограниченной длины без ВЫРАЗИТЬ — ошибка при выполнении.
  • В ПОДОБНО передан параметр без знаков % — условие работает как точное равенство.
  • Пользовательский ввод с символами %, _, [ не экранирован — поиск находит лишнее или падает.
  • Слагаемое со значением NULL «обнуляет» всю конкатенацию.
  • Слишком короткий СТРОКА(N) молча обрезает данные.
  • Расчёт на то, что ПОДСТРОКА использует индекс — для поиска по началу применяйте ПОДОБНО.

Шаблоны ПОДОБНО: шпаргалка

ШаблонЧто находит
"кабель%"Строки, начинающиеся с «кабель»
"%кабель"Строки, заканчивающиеся на «кабель»
"%кабель%"Строки, содержащие «кабель»
"К_бель"«Кабель», «Кобель» — любой один символ на месте подчёркивания
"[а-я]%"Начинается со строчной русской буквы
"[^0-9]%"Не начинается с цифры
"%\%" СПЕЦСИМВОЛ "\"Заканчивается знаком процента

Актуальность для новых версий

Оператор ПОДОБНО, функции ПОДСТРОКА и ВЫРАЗИТЬ работают одинаково во всех версиях платформы 8.3. В новых релизах 8.3 язык запросов получил дополнительные строковые функции (СТРДЛИНА, СТРНАЙТИ, СТРЗАМЕНИТЬ, ВРЕГ, НРЕГ, СОКРЛП, ЛЕВ, ПРАВ) — перед использованием проверьте их наличие в синтакс-помощнике своего релиза. Запросы, написанные только на ПОДОБНО, ПОДСТРОКА и ВЫРАЗИТЬ, совместимы с любой версией 8.3.

Частые вопросы

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

Приведите его к строке ограниченной длины: ВЫРАЗИТЬ(Поле КАК СТРОКА(200)) = &Значение или ПОДСТРОКА(Поле, 1, 200) = &Значение. Напрямую такие поля нельзя сравнивать, группировать и упорядочивать.

Как найти в запросе 1С строки, содержащие знак процента или подчёркивание?

Добавьте после шаблона ключевое слово СПЕЦСИМВОЛ и символ-экранировщик: Поле ПОДОБНО "%50\%%" СПЕЦСИМВОЛ "\". Экранированный знак воспринимается как обычный текст.

Зачем в ПОДОБНО нужны знаки процента?

Знак % заменяет любую последовательность символов. Без него ПОДОБНО работает как точное сравнение: "Кабель" найдёт только строку «Кабель», а "%кабель%" — любую строку, где есть это слово.

Почему конкатенация в запросе возвращает NULL?

Если одно из слагаемых равно NULL, например после левого соединения, вся сумма строк тоже становится NULL. Оберните такие поля в ЕСТЬNULL(Поле, "").

Ссылка в запросе 1С: разыменование, ВЫРАЗИТЬ, пустая ссылка и GUID

Запросы

Практический разбор ссылок в языке запросов 1С: разыменование и его цена, ВЫРАЗИТЬ и ТИПЗНАЧЕНИЯ для составных типов, пустая ссылка и NULL, представление и уникальный идентификатор.

Дата в запросе 1С: ДАТАВРЕМЯ, период, МЕЖДУ, пустая и текущая дата

Запросы

Все приёмы работы с датами в языке запросов 1С: литералы и параметры, отбор за период, дата без времени, извлечение года и месяца, разность и сдвиг дат, пустая и текущая дата.

Запросы в 1С: как написать запрос, передать параметры и обработать результат

Запросы

Пошаговый разбор работы с запросами в 1С: текст и параметры, обход результата и выгрузка в таблицу значений, пакетные запросы с временными таблицами и отладка в консоли запросов.

Запрос 1С, который генерирует даты по дням за период

Запросы

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