Как корректно использовать функцию МАКСИМУМ для строковых номеров и кодов в запросах 1С?

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

При разработке механизмов генерации уникальных номеров и кодов (например, для подарочных сертификатов, дисконтных карт или штрихкодов вида ххх/уууууу) часто возникает соблазн использовать агрегатную функцию МАКСИМУМ прямо в тексте запроса по строковому полю. Разберем детально, как СУБД выполняет строковое сравнение, при каких условиях получение максимума работает стабильно, какие скрытые риски несет данный подход и какие архитектурные альтернативы стоит применять.

Особенности лексикографического сравнения строк в СУБД

В языке запросов 1С агрегатная функция МАКСИМУМ(Поле) применительно к типу Строка возвращает последнее значение при упорядочивании по возрастанию. Строковое сравнение всегда выполняется посимвольно (слева направо) по кодам символов, а не по их математическому значению.

Проанализируем базовый пример:

  1. Если сравнивать числа 3 и 11, число 11 больше числа 3.
  2. Если сравнивать строки "3" и "11", то на первой же позиции символ '3' имеет больший код, чем символ '1'. Следовательно, выражение "3" > "11" вернет Истина.
  3. Если строки дополнены лидирующими нулями до одинаковой разрядности ("03" и "11"), то на первой позиции символ '0' меньше символа '1', поэтому "11" > "03".

Таким образом, функция МАКСИМУМ для строковых кодов выдает ожидаемый результат только при строгом соблюдении следующих условий:

  1. Строго фиксированная длина числовой части: все номера обязаны иметь одинаковое количество знаков.
  2. Обязательное наличие лидирующих нулей: пустые старшие разряды должны быть заполнены символами '0'.
  3. Одинаковая длина и неизменность префикса: префиксная часть (например, "XXX/") должна быть строго постоянной в рамках выборки. Если префиксы отличаются по длине (например, "A/0001" и "AA/0001"), строковое сравнение нарушится.

Пример безопасного получения максимального номера в запросе

Если префиксы в базе могут быть разными, в запросе обязательно нужно отфильтровать выборку по конкретному префиксу. Рассмотрим пример запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	МАКСИМУМ(Сертификаты.Код) КАК МаксимальныйКод
    |ИЗ
    |	Справочник.ПодарочныеСертификаты КАК Сертификаты
    |ГДЕ
    |	Сертификаты.Код ПОДОБНО &ШаблонКода";

Запрос.УстановитьПараметр("ШаблонКода", ПрефиксСертификата + "/%");
Выборка = Запрос.Выполнить().Выбрать();

Если в запросе требуется выделить только числовую часть кода постоянной длины, можно использовать функцию ПОДСТРОКА:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	МАКСИМУМ(ПОДСТРОКА(Сертификаты.Код, 5, 6)) КАК МаксимальныйНомер
    |ИЗ
    |	Справочник.ПодарочныеСертификаты КАК Сертификаты
    |ГДЕ
    |	Сертификаты.Код ПОДОБНО &ШаблонКода";

Запрос.УстановитьПараметр("ШаблонКода", ПрефиксСертификата + "/%");

Подводные камни и риски строкового максимума

Даже при наличии лидирующих нулей важно учитывать следующие технические нюансы:

  1. Переполнение разрядности: как только нумерация исчерпает текущий лимит знаков (например, после номера "999999" пойдет "1000000"), строка "999999" всегда будет признаваться большей, чем "1000000", так как символ '9' старше символа '1'.
  2. Невидимые и пробельные символы: если код вводится или редактируется пользователями вручную, в начало или середину строки могут попасть обычные или неразрывные пробелы (код 160). Код пробела меньше кодов цифр, что искажает результаты сортировки.
  3. Параметры сортировки СУБД (Collation): в многоязычных базах или при миграции между MS SQL Server и PostgreSQL правила сопоставления спецсимволов (слэши, дефисы) и букв национальных алфавитов могут отличаться.
  4. Конкурентный доступ: простое выполнение запроса перед записью не защищает от дублирования номеров в многопользовательском режиме. Две параллельные транзакции могут прочитать одинаковый максимальный номер и попытаться записать дубли. Для решения этой проблемы необходимо устанавливать управляемую блокировку через БлокировкаДанных в пределах транзакции.

Альтернативные архитектурные решения

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

1. Раздельное хранение данных

Храните порядковый номер в отдельном индексированном реквизите с типом Число. Полное строковое представление (например, "XXX/000125") формируйте программно при записи объекта или храните в реквизите Код только для отображения. Поиск максимума по числовому полю в СУБД работает значительно быстрее и гарантирует математическую точность.

2. Использование штатной автонумерации 1С

Платформа 1С содержит встроенные механизмы автонумерации (методы УстановитьНовыйКод() и УстановитьНовыйНомер()). Платформа самостоятельно находит числовую часть в конце строки, сохраняет лидирующие нули и увеличивает значение на единицу. Если автонумерация сбилась, её можно восстановить вызовом метода глобального контекста ОбновитьНумерациюОбъектов().

3. Использование регистра сведений для генерации номеров

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

← На главную