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

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

В практике разработки на платформе 1С часто возникает ситуация, когда данные в базе хранятся в строковом виде (например, Наименование или Номер), но по факту представляют собой числовую последовательность с ведущими нулями: "0000001", "0000002", "0000010". При попытке использовать стандартные операторы сравнения БОЛЬШЕ ИЛИ РАВНО (>=) или МЕЖДУ в запросах, программисты сталкиваются с тем, что строки сравниваются посимвольно (лексикографически), а не математически. В результате строка "0002" оказывается больше, чем "00010", что ломает логику построения интервальных отчетов.

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

Проблема лексикографического сравнения

Прежде чем перейти к решениям, проанализируем ситуацию: в SQL-базах данных сравнение строк идет слева направо по кодам символов. Для строк одинаковой длины с ведущими нулями это работает корректно. Однако, если пользователь вводит в поле поиска номер "123", а в базе хранятся значения "000123", прямое сравнение выдаст непредсказуемый результат. Еще хуже ситуация, когда в базе встречаются номера разной длины или номера с префиксами. Выясним причину: программа сравнивает первый символ первой строки с первым символом второй, затем вторые и так далее. Как только находится различие, сравнение прекращается.

Метод 1: Использование вспомогательного числового реквизита (Лучшая практика)

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

Разберем алгоритм реализации этого метода:

  1. Создание реквизита: Добавьте в метаданные реквизит ЧисловойНомер с типом Число соответствующей длины.
  2. Автоматическое заполнение: В модуле объекта в обработчике события ПередЗаписью пропишите логику конвертации строки в число. Это позволит всегда держать данные в актуальном состоянии.

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Очищаем строку от лишних символов и преобразуем в число
    // Используем попытку, так как в наименовании могут быть не только цифры
    Попытка
        ЧисловойНомер = Число(Наименование);
    Исключение
        ЧисловойНомер = 0;
    КонецПопытки;
КонецПроцедуры

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


ТекстЗапроса = "ВЫБРАТЬ
               |    Т.Наименование КАК НомерСтрокой,
               |    Т.Ссылка КАК Ссылка
               |ИЗ
               |    Справочник.ВашСправочник КАК Т
               |ГДЕ
               |    Т.ЧисловойНомер МЕЖДУ &НачалоИнтервала И &КонецИнтервала";

Запрос.УстановитьПараметр("НачалоИнтервала", Число(Объект.НомерНачало));
Запрос.УстановитьПараметр("КонецИнтервала", Число(Объект.НомерКонец));

Метод 2: Приведение строк к фиксированной длине в запросе

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

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


// В коде 1С подготовим параметры, тоже дополнив их нулями
Функция ДополнитьСтрокуНулями(Знач Стр, Длина)
    Пока СтрДлина(Стр) < Длина Цикл
        Стр = "0" + Стр;
    КонецЦикла;
    Возврат Стр;
КонецФункции

ПараметрНачало = ДополнитьСтрокуНулями(НомерНачало, 10);
ПараметрКонец = ДополнитьСтрокуНулями(НомерКонец, 10);

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

Внимание: Этот метод имеет существенный недостаток. Использование функций над полями таблицы в блоке ГДЕ делает запрос неоптимизируемым (индексы по полю Наименование не будут использоваться). Это приведет к полному сканированию таблицы (Table Scan), что на больших объемах данных (сотни тысяч записей) вызовет серьезные задержки.

Метод 3: Обработка параметров на стороне сервера 1С

Если вы точно знаете, что в базе все номера хранятся в строгом формате (например, всегда 8 знаков с ведущими нулями), то задача упрощается до правильной подготовки параметров. Вам не нужно менять сам запрос — достаточно привести введенные пользователем значения на форме к формату хранения в БД.

Проанализируем ситуацию: пользователь ввел "123", а в базе "00000123". Мы должны программно превратить "123" в "00000123" перед передачей в запрос. Для этого используем описание типов или строковые функции 1С.


// Пример подготовки параметров
ОписаниеТипаЧисло = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0));
ЧисловоеЗначение = ОписаниеТипаЧисло.ПривестиЗначение(НомерНачало);

// Форматируем обратно в строку с нужной длиной
СтрокаДляЗапроса = Формат(ЧисловоеЗначение, "ЧГ=0; ЧВН=; ЧЦ=10"); 
// Параметр ЧВН добавит ведущие нули
Запрос.УстановитьПараметр("НомерНачало", СтрокаДляЗапроса);

Метод 4: Сложная конвертация строки в число в запросе

В редких случаях, когда нужно именно математическое сравнение "на лету" и нет возможности подготовить данные, программисты используют громоздкие конструкции ВЫБОР для преобразования каждого разряда строки в число. Это "тяжелый" путь, но он существует. Мы разбираем его для полноты картины. Каждый символ строки проверяется на соответствие цифре и умножается на соответствующий вес разряда (10, 100, 1000 и т.д.).


// Фрагмент формирования числа из строки (для последних 3-х разрядов)
"ВЫБРАТЬ
|    (ВЫБОР ПОДСТРОКА(Т.Наименование, 1, 1) КОГДА ""1"" ТОГДА 1 КОГДА ""2"" ТОГДА 2 ... КОНЕЦ) * 100 +
|    (ВЫБОР ПОДСТРОКА(Т.Наименование, 2, 1) КОГДА ""1"" ТОГДА 1 КОГДА ""2"" ТОГДА 2 ... КОНЕЦ) * 10 +
|    (ВЫБОР ПОДСТРОКА(Т.Наименование, 3, 1) КОГДА ""1"" ТОГДА 1 КОГДА ""2"" ТОГДА 2 ... КОНЕЦ)
| КАК ЧисловойРезультат"

Такой подход крайне сложно поддерживать, и он работает только для строк фиксированной и небольшой длины. Если в строке встретится нецифровой символ (например, пробел или разделитель), запрос может завершиться с ошибкой или вернуть некорректный результат.

Резюме и рекомендации

Подводя итоги, можно выделить следующие правила работы с интервалами строковых чисел:

  1. Всегда отдавайте предпочтение Методу 1 (числовой реквизит). Это стандарт индустрии, обеспечивающий быстродействие и чистоту кода.
  2. Если работа идет с типовой конфигурацией, где нельзя менять структуру, используйте Метод 3 (подготовка параметров), при условии, что формат номеров в базе стабилен.
  3. Если номера имеют разную длину и хранятся "как попало", используйте временные таблицы. Сначала выберите широкий диапазон строк, выгрузите их в ТаблицуЗначений, добавьте там колонку с числом (через функцию Число() языка 1С) и выполните фильтрацию уже в памяти сервера.

Помните, что корректная работа с типами данных на этапе проектирования избавляет от необходимости писать сложные и медленные запросы в будущем. Рациональное использование индексов и понимание того, как SQL сравнивает строки, — ключ к созданию быстрых и надежных отчетов в 1С.

← На главную