В практике разработки на платформе 1С нередко возникает задача сформировать порядковые номера записей непосредственно внутри запроса. Ситуация усложняется, когда сортировка выполняется по нескольким разнотипным полям (например, строка, дата и число), а использование конструкции АвтономерЗаписи невозможно из-за требований к структуре запроса или условий задачи. Разберем по шагам, как решить эту проблему с помощью языка запросов и встроенного языка платформы.
Проанализируем суть математической нумерации через реляционные операции: чтобы узнать порядковый номер текущей строки при заданной сортировке, достаточно подсчитать, сколько строк в этой же таблице предшествуют ей или равны ей. Для этого исходная таблица соединяется сама с собой левым соединением.
Когда сортировка выполняется по нескольким колонкам (в нашем примере: Наименование, Дата, Код), условие предшествования одной строки другой описывается каскадом логических операторов И и ИЛИ без преобразования типов данных. Рассмотрим текст запроса:
ВЫБРАТЬ
"Облако" КАК Наименование,
ДАТАВРЕМЯ(2022, 2, 1) КАК Дата,
123 КАК Код
ПОМЕСТИТЬ ТаблицаДанных
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Аквамарин",
ДАТАВРЕМЯ(2022, 2, 2),
100
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Пробел",
ДАТАВРЕМЯ(2022, 1, 7),
20
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Машина",
ДАТАВРЕМЯ(2022, 1, 10),
15
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Строка",
ДАТАВРЕМЯ(2022, 1, 5),
10
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Пробел",
ДАТАВРЕМЯ(2022, 1, 7),
100
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Статика",
ДАТАВРЕМЯ(2022, 1, 15),
87
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Интер",
ДАТАВРЕМЯ(2022, 2, 5),
50
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Интер",
ДАТАВРЕМЯ(2022, 2, 7),
34
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т1.Наименование КАК Наименование,
Т1.Дата КАК Дата,
Т1.Код КАК Код,
КОЛИЧЕСТВО(Т2.Код) КАК НомерСтроки
ИЗ
ТаблицаДанных КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаДанных КАК Т2
ПО (Т1.Наименование > Т2.Наименование)
ИЛИ (Т1.Наименование = Т2.Наименование И Т1.Дата > Т2.Дата)
ИЛИ (Т1.Наименование = Т2.Наименование И Т1.Дата = Т2.Дата И Т1.Код >= Т2.Код)
СГРУППИРОВАТЬ ПО
Т1.Наименование,
Т1.Дата,
Т1.Код
УПОРЯДОЧИТЬ ПО
Наименование,
Дата,
Код
Разберем подробнее, почему этот способ является наиболее надежным:
КОЛИЧЕСТВО(Т2.Код) без использования ресурсоемкого модификатора РАЗЛИЧНЫЕ.Рассмотрим альтернативное решение, предложенное участниками обсуждения. Идея состоит в том, чтобы склеить все поля сортировки в единую текстовую строку фиксированного формата во временной таблице, после чего выполнить левое соединение по нестрогому неравенству ВТПоля.Ключ >= ВТПоля1.Ключ:
ВЫБРАТЬ
ТаблицаДанных.Наименование КАК Наименование,
ТаблицаДанных.Дата КАК Дата,
ТаблицаДанных.Код КАК Код,
ТаблицаДанных.Наименование + СТРОКА(ТаблицаДанных.Дата) + ВЫБОР
КОГДА ДЛИНАСТРОКИ(СТРОКА(ТаблицаДанных.Код)) = 1
ТОГДА "0000" + СТРОКА(ТаблицаДанных.Код)
КОГДА ДЛИНАСТРОКИ(СТРОКА(ТаблицаДанных.Код)) = 2
ТОГДА "000" + СТРОКА(ТаблицаДанных.Код)
КОГДА ДЛИНАСТРОКИ(СТРОКА(ТаблицаДанных.Код)) = 3
ТОГДА "00" + СТРОКА(ТаблицаДанных.Код)
КОГДА ДЛИНАСТРОКИ(СТРОКА(ТаблицаДанных.Код)) = 4
ТОГДА "0" + СТРОКА(ТаблицаДанных.Код)
КОНЕЦ КАК Ключ
ПОМЕСТИТЬ ВТПоля
ИЗ
ТаблицаДанных КАК ТаблицаДанных
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТПоля.Наименование КАК Наименование,
ВТПоля.Дата КАК Дата,
ВТПоля.Код КАК Код,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТПоля1.Ключ) КАК Номер
ИЗ
ВТПоля КАК ВТПоля
ЛЕВОЕ СОЕДИНЕНИЕ ВТПоля КАК ВТПоля1
ПО (ВТПоля.Ключ >= ВТПоля1.Ключ)
СГРУППИРОВАТЬ ПО
ВТПоля.Наименование,
ВТПоля.Дата,
ВТПоля.Код
УПОРЯДОЧИТЬ ПО
Наименование,
Дата,
Код
Выясним причину, почему данный способ требует повышенной осторожности и на практике часто приводит к скрытым ошибкам:
СТРОКА(Дата) преобразует значение в формат с днем на первом месте (например, «01.02.2022»). При алфавитном сравнении строка «01.02.2022» окажется меньше, чем «07.01.2022», что полностью сломает хронологическую сортировку.СТРОКА() для приведения типов в блоке ВЫБРАТЬ доступно только начиная с релиза 8.3.20 (при отключенном режиме совместимости).ДЛИНАСТРОКИ жестко зашито под фиксированное число знаков. Если в коде окажется больше знаков, сортировка нарушится.Важно помнить, что любые способы нумерации внутри запроса через соединение таблицы с самой собой по условиям >= имеют алгоритмическую сложность O(N²). На таблице из 100 строк СУБД обработает порядка 10 000 сравнений, однако на выборке в 50 000 строк объем соединяемых данных превысит миллиарды записей, что приведет к переполнению диска и зависанию сеанса. Поэтому в реальных рабочих конфигурациях нумерацию запросом применяют только к небольшим порциям данных.
Если нет жесткого требования формировать номер исключительно средствами SQL-запроса, проанализируем гораздо более производительные штатные инструменты:
НомерПоПорядку или НомерПоПорядкуВГруппировке(). В этом случае нумерация выполняется линейно ядром компоновки без создания тяжелых запросов.
Выборка = Запрос.Выполнить().Выбрать();
Номер = 0;
Пока Выборка.Следующий() Цикл
Номер = Номер + 1;
// Использование переменной Номер
КонецЦикла;
ТаблицаЗначений: если результат выгружен в память, добавляется колонка и заполняется простым циклом:
ТЗ = Запрос.Выполнить().Выгрузить();
ТЗ.Колонки.Добавить("НомерСтроки", Новый ОписаниеТипов("Число"));
Для Индекс = 0 По ТЗ.Количество() - 1 Цикл
ТЗ[Индекс].НомерСтроки = Индекс + 1;
КонецЦикла;