В практике разработки на платформе 1С часто возникает ситуация, когда данные в базе хранятся в строковом виде (например, Наименование или Номер), но по факту представляют собой числовую последовательность с ведущими нулями: "0000001", "0000002", "0000010". При попытке использовать стандартные операторы сравнения БОЛЬШЕ ИЛИ РАВНО (>=) или МЕЖДУ в запросах, программисты сталкиваются с тем, что строки сравниваются посимвольно (лексикографически), а не математически. В результате строка "0002" оказывается больше, чем "00010", что ломает логику построения интервальных отчетов.
Рассмотрим подробнее, почему это происходит, и разберем по шагам наиболее эффективные способы решения этой задачи, от "быстрых правок" в запросе до правильного архитектурного подхода.
Прежде чем перейти к решениям, проанализируем ситуацию: в SQL-базах данных сравнение строк идет слева направо по кодам символов. Для строк одинаковой длины с ведущими нулями это работает корректно. Однако, если пользователь вводит в поле поиска номер "123", а в базе хранятся значения "000123", прямое сравнение выдаст непредсказуемый результат. Еще хуже ситуация, когда в базе встречаются номера разной длины или номера с префиксами. Выясним причину: программа сравнивает первый символ первой строки с первым символом второй, затем вторые и так далее. Как только находится различие, сравнение прекращается.
Наиболее надежным и производительным способом является разделение представления данных и логики поиска. Если ваша задача — часто строить отчеты по диапазонам номеров, мы настоятельно рекомендуем добавить в объект (справочник или документ) дополнительный реквизит с типом Число. Назовем его, например, ЧисловойНомер.
Разберем алгоритм реализации этого метода:
ЧисловойНомер с типом Число соответствующей длины.ПередЗаписью пропишите логику конвертации строки в число. Это позволит всегда держать данные в актуальном состоянии.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Очищаем строку от лишних символов и преобразуем в число
// Используем попытку, так как в наименовании могут быть не только цифры
Попытка
ЧисловойНомер = Число(Наименование);
Исключение
ЧисловойНомер = 0;
КонецПопытки;
КонецПроцедуры
Теперь в запросе для отбора по интервалу мы будем использовать именно этот числовой реквизит, что обеспечит максимальную скорость работы за счет индексации числового поля. При этом в самом отчете пользователю можно продолжать выводить строковое Наименование.
ТекстЗапроса = "ВЫБРАТЬ
| Т.Наименование КАК НомерСтрокой,
| Т.Ссылка КАК Ссылка
|ИЗ
| Справочник.ВашСправочник КАК Т
|ГДЕ
| Т.ЧисловойНомер МЕЖДУ &НачалоИнтервала И &КонецИнтервала";
Запрос.УстановитьПараметр("НачалоИнтервала", Число(Объект.НомерНачало));
Запрос.УстановитьПараметр("КонецИнтервала", Число(Объект.НомерКонец));
Если изменять структуру данных (добавлять реквизиты) нельзя, можно прибегнуть к "хитрости" в языке запросов. Идея заключается в том, чтобы программно дополнить сравниваемые строки ведущими нулями до определенной длины прямо в момент выполнения запроса. Если все строки будут иметь одинаковую длину (например, 15 символов), лексикографическое сравнение будет полностью совпадать с математическим.
Посмотрим на пример реализации такого дополнения с помощью функций ПОДСТРОКА и ПРАВО (в языке запросов 1С функция ПРАВО имитируется через комбинации строк). Рассмотрим упрощенный пример, где мы дополняем номер до 10 знаков:
// В коде 1С подготовим параметры, тоже дополнив их нулями
Функция ДополнитьСтрокуНулями(Знач Стр, Длина)
Пока СтрДлина(Стр) < Длина Цикл
Стр = "0" + Стр;
КонецЦикла;
Возврат Стр;
КонецФункции
ПараметрНачало = ДополнитьСтрокуНулями(НомерНачало, 10);
ПараметрКонец = ДополнитьСтрокуНулями(НомерКонец, 10);
// В запросе используем логику дополнения для поля таблицы
ТекстЗапроса = "ВЫБРАТЬ
| Т.Наименование
|ИЗ
| Документ.ВашДокумент.Пл КАК Т
|ГДЕ
| ПОДСТРОКА(""0000000000"", 1, 10 - ДЛИНАСТРОКИ(Т.Наименование)) + Т.Наименование
| МЕЖДУ &Начало И &Конец";
Внимание: Этот метод имеет существенный недостаток. Использование функций над полями таблицы в блоке ГДЕ делает запрос неоптимизируемым (индексы по полю Наименование не будут использоваться). Это приведет к полному сканированию таблицы (Table Scan), что на больших объемах данных (сотни тысяч записей) вызовет серьезные задержки.
Если вы точно знаете, что в базе все номера хранятся в строгом формате (например, всегда 8 знаков с ведущими нулями), то задача упрощается до правильной подготовки параметров. Вам не нужно менять сам запрос — достаточно привести введенные пользователем значения на форме к формату хранения в БД.
Проанализируем ситуацию: пользователь ввел "123", а в базе "00000123". Мы должны программно превратить "123" в "00000123" перед передачей в запрос. Для этого используем описание типов или строковые функции 1С.
// Пример подготовки параметров
ОписаниеТипаЧисло = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0));
ЧисловоеЗначение = ОписаниеТипаЧисло.ПривестиЗначение(НомерНачало);
// Форматируем обратно в строку с нужной длиной
СтрокаДляЗапроса = Формат(ЧисловоеЗначение, "ЧГ=0; ЧВН=; ЧЦ=10");
// Параметр ЧВН добавит ведущие нули
Запрос.УстановитьПараметр("НомерНачало", СтрокаДляЗапроса);
В редких случаях, когда нужно именно математическое сравнение "на лету" и нет возможности подготовить данные, программисты используют громоздкие конструкции ВЫБОР для преобразования каждого разряда строки в число. Это "тяжелый" путь, но он существует. Мы разбираем его для полноты картины. Каждый символ строки проверяется на соответствие цифре и умножается на соответствующий вес разряда (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С) и выполните фильтрацию уже в памяти сервера.Помните, что корректная работа с типами данных на этапе проектирования избавляет от необходимости писать сложные и медленные запросы в будущем. Рациональное использование индексов и понимание того, как SQL сравнивает строки, — ключ к созданию быстрых и надежных отчетов в 1С.