В процессе разработки на платформе «1С:Предприятие» перед программистами регулярно возникает задача нумерации строк непосредственно в запросе. Долгое время для этого приходилось использовать сложные, неоптимальные обходные пути. Ситуация кардинально изменилась с выходом технологической платформы версии 8.3.13, в которой появился штатный механизм автонумерации строк.
В этой статье мы подробно разберем, начиная с какого релиза доступна функция АВТОНОМЕРЗАПИСИ(), как выглядит её синтаксис, с какими критическими «ловушками» СУБД можно столкнуться при её использовании и как эта функция соотносится с другими механизмами платформы, такими как группирующие наборы GROUPING SETS.
Возможность создания поля с уникальными, последовательно возрастающими значениями была реализована в платформе «1С:Предприятие» начиная с версии 8.3.13.1513.
Основная цель внедрения этой функции — упростить и ускорить процесс создания монотонно возрастающего уникального ключевого поля для временных таблиц. До появления этого механизма разработчикам приходилось жертвовать производительностью базы данных или писать избыточный код на встроенном языке.
Функция АВТОНОМЕРЗАПИСИ() работает на уровне СУБД и транслируется в создание автоинкрементного поля. Однако платформа накладывает ряд строгих ограничений на её применение:
ПОМЕСТИТЬ ИмяВременнойТаблицы). В обычных выборках, возвращающих результат в код, использовать её нельзя.АВТОНОМЕРЗАПИСИ() + 1) или в условиях связи/отбора.ОБЪЕДИНИТЬ или ОБЪЕДИНИТЬ ВСЕ на самом верхнем уровне иерархии запроса.АВТОНОМЕРЗАПИСИ(). Создать две независимые колонки со счетчиками в одной таблице физически невозможно.Число).Рассмотрим пример правильного использования функции при создании временной таблицы:
ВЫБРАТЬ
АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
СпрНоменклатура.Ссылка КАК Номенклатура,
СпрНоменклатура.Наименование КАК Наименование
ПОМЕСТИТЬ ВТ_НумерованныйСписок
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ГДЕ
СпрНоменклатура.ЭтоГруппа = ЛОЖЬ
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.НомерСтроки,
ВТ.Номенклатура,
ВТ.Наименование
ИЗ
ВТ_НумерованныйСписок КАК ВТ
Многие разработчики совершают опасную ошибку, полагая, что нумерация в таблице всегда гарантированно начинается с единицы. В официальной документации указано: «Начальное значение счетчика зависит от используемой СУБД и, в общем случае, может быть любым». На практике это выливается в серьезную проблему, связанную с кэшированием соединений в СУБД (MS SQL Server, PostgreSQL).
Проанализируем, как ведет себя счетчик при повторных запусках запроса в рамках одного сеанса:
Где это критично?
Во-первых, при использовании функции в динамических списках или при проведении документов, где разработчик делает отбор вида ГДЕ НомерСтроки = 1 (например, для защиты от дублирования записей). При быстром перепроведении документа запись может «исчезнуть» из итоговой выборки, так как её номер внезапно станет равен 6 вместо ожидаемого 1, а спустя 10 минут «магическим» образом вернуться назад.
Во-вторых, при соединении двух временных таблиц по полю автонумерации. Если в одной таблице счетчик начался с 1, а во второй (из-за кэширования в параллельном процессе) — с 20, то в результате соединения вы получите абсолютно пустую таблицу или некорректно сопоставленные данные.
Важная особенность отладки: В файловом режиме работы базы данных (на локальном компьютере разработчика) этот эффект практически не воспроизводится — там нумерация почти всегда стабильно начинается с 1. Это усыпляет бдительность, и критическая ошибка уходит на рабочий клиент-серверный сервер.
Чтобы понять ценность появления АВТОНОМЕРЗАПИСИ(), вспомним, какие обходные пути приходилось использовать программистам ранее, и проанализируем их недостатки:
Т1.УникальныйКлюч >= Т2.УникальныйКлюч, после чего выполнялась группировка и подсчет количества строк.
Посмотрим на пример такого запроса:
ВЫБРАТЬ
Т1.Ссылка КАК Номенклатура,
КОЛИЧЕСТВО(Т2.Ссылка) КАК НомерСтроки
ИЗ
Справочник.Номенклатура КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Т2
ПО Т1.Ссылка >= Т2.Ссылка
СГРУППИРОВАТЬ ПО
Т1.Ссылка
Этот метод имеет вычислительную сложность O(N²). Если в таблице несколько десятков тысяч строк, такой запрос гарантированно приводил к колоссальному падению производительности, перегрузке процессора СУБД и даже к зависанию всей системы.
СистемныеПоля.НомерПоПорядку. Это работает быстро, но применимо исключительно при выводе данных в отчеты компоновки и бесполезно при разработке бизнес-логики в модулях проведения.Таблицу Значений, после чего колонка с номерами заполнялась в цикле Для каждого. Это надежный метод, но он требует перегона всего объема данных на сторону сервера приложений 1С и лишает возможности использовать пронумерованные данные в последующих пакетных запросах СУБД.Внедрение АВТОНОМЕРЗАПИСИ() перенесло задачу нумерации на уровень СУБД (где она транслируется в эффективные нативные инструкции), что позволило обрабатывать миллионы строк за доли секунды.
Механизм Дата-акселератора (используемый для аналитических расчетов больших объемов данных в оперативной памяти) на первых этапах имел жесткое ограничение: он не умел обрабатывать запросы, содержащие одновременно обращение к табличным частям объектов и функцию АВТОНОМЕРЗАПИСИ(). В современных версиях платформы это ограничение было успешно преодолено, и работа с автонумерацией поддерживается в полном объеме.
Развивая аналитические возможности языка запросов, фирма «1С» в релизе 8.3.16 добавила еще один мощный инструмент агрегации данных — группирующие наборы.
До версии 8.3.16 для получения многомерных итогов по разным разрезам (например, когда в одном результате нужно получить продажи одновременно по комбинации «Номенклатура + Склад», просто по «Номенклатуре», просто по «Складу» и общий итог) приходилось писать несколько запросов с последующим объединением через тяжелую конструкцию ОБЪЕДИНИТЬ ВСЕ.
Начиная с релиза 8.3.16, конструкция ГРУППИРОВАТЬ ПО поддерживает расширения:
GROUPING SETS (Группирующие наборы) — позволяет явно перечислить нужные комбинации измерений для расчета итогов.ROLLUP (Расчет итогов по иерархии) — строит промежуточные итоги, последовательно сворачивая измерения справа налево.CUBE (Многомерный куб) — автоматически строит итоги по абсолютно всем возможным сочетаниям указанных колонок.Проанализируем пример использования группирующих наборов:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
Продажи.Склад КАК Склад,
СУММА(Продажи.СуммаОбороты) КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты КАК Продажи
СГРУППИРОВАТЬ ПО
ГРУППИРУЮЩИЕ НАБОРЫ (
(Продажи.Номенклатура, Продажи.Склад),
(Продажи.Номенклатура),
(Продажи.Склад),
()
)
Преимущество этого подхода заключается в том, что СУБД выполняет такой запрос за один проход по физической таблице данных, что работает в разы быстрее старых схем с множественными объединениями запросов.
Подведем итог нашему разбору:
АВТОНОМЕРЗАПИСИ() можно без опасений, если вы работаете на платформе версии 8.3.13.1513 и выше.АВТОНОМЕРЗАПИСИ() во временных таблицах и GROUPING SETS (доступных с версии 8.3.16) для построения высокопроизводительных аналитических алгоритмов без перегрузки СУБД.