При разработке механизмов генерации уникальных номеров и кодов (например, для подарочных сертификатов, дисконтных карт или штрихкодов вида ххх/уууууу) часто возникает соблазн использовать агрегатную функцию МАКСИМУМ прямо в тексте запроса по строковому полю. Разберем детально, как СУБД выполняет строковое сравнение, при каких условиях получение максимума работает стабильно, какие скрытые риски несет данный подход и какие архитектурные альтернативы стоит применять.
В языке запросов 1С агрегатная функция МАКСИМУМ(Поле) применительно к типу Строка возвращает последнее значение при упорядочивании по возрастанию. Строковое сравнение всегда выполняется посимвольно (слева направо) по кодам символов, а не по их математическому значению.
Проанализируем базовый пример:
3 и 11, число 11 больше числа 3."3" и "11", то на первой же позиции символ '3' имеет больший код, чем символ '1'. Следовательно, выражение "3" > "11" вернет Истина."03" и "11"), то на первой позиции символ '0' меньше символа '1', поэтому "11" > "03".Таким образом, функция МАКСИМУМ для строковых кодов выдает ожидаемый результат только при строгом соблюдении следующих условий:
'0'."XXX/") должна быть строго постоянной в рамках выборки. Если префиксы отличаются по длине (например, "A/0001" и "AA/0001"), строковое сравнение нарушится.Если префиксы в базе могут быть разными, в запросе обязательно нужно отфильтровать выборку по конкретному префиксу. Рассмотрим пример запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МАКСИМУМ(Сертификаты.Код) КАК МаксимальныйКод
|ИЗ
| Справочник.ПодарочныеСертификаты КАК Сертификаты
|ГДЕ
| Сертификаты.Код ПОДОБНО &ШаблонКода";
Запрос.УстановитьПараметр("ШаблонКода", ПрефиксСертификата + "/%");
Выборка = Запрос.Выполнить().Выбрать();
Если в запросе требуется выделить только числовую часть кода постоянной длины, можно использовать функцию ПОДСТРОКА:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МАКСИМУМ(ПОДСТРОКА(Сертификаты.Код, 5, 6)) КАК МаксимальныйНомер
|ИЗ
| Справочник.ПодарочныеСертификаты КАК Сертификаты
|ГДЕ
| Сертификаты.Код ПОДОБНО &ШаблонКода";
Запрос.УстановитьПараметр("ШаблонКода", ПрефиксСертификата + "/%");
Даже при наличии лидирующих нулей важно учитывать следующие технические нюансы:
"999999" пойдет "1000000"), строка "999999" всегда будет признаваться большей, чем "1000000", так как символ '9' старше символа '1'.БлокировкаДанных в пределах транзакции.Чтобы избежать проблем с лексикографическим сравнением и повысить надежность системы, рекомендуем рассмотреть следующие подходы:
1. Раздельное хранение данных
Храните порядковый номер в отдельном индексированном реквизите с типом Число. Полное строковое представление (например, "XXX/000125") формируйте программно при записи объекта или храните в реквизите Код только для отображения. Поиск максимума по числовому полю в СУБД работает значительно быстрее и гарантирует математическую точность.
2. Использование штатной автонумерации 1С
Платформа 1С содержит встроенные механизмы автонумерации (методы УстановитьНовыйКод() и УстановитьНовыйНомер()). Платформа самостоятельно находит числовую часть в конце строки, сохраняет лидирующие нули и увеличивает значение на единицу. Если автонумерация сбилась, её можно восстановить вызовом метода глобального контекста ОбновитьНумерациюОбъектов().
3. Использование регистра сведений для генерации номеров
Создайте регистр сведений, где в измерениях хранится вид объекта или префикс, а в ресурсе — текущий целочисленный номер. В транзакции создания объекта выполняется блокировка записи регистра, чтение текущего номера, увеличение его на 1 и запись обратно. Это полностью исключает сканирование справочника и коллизии номеров при параллельной работе пользователей.