7 способов сделать генератор чисел в SQL
Рассказали о 7 способах генерации последовательности чисел в заданном диапазоне в SQL и сравнили их производительность.
Расскажем о способах генерирования последовательности чисел в заданном диапазоне в SQL. И сравним их производительность.
Задача
Требуется сгенерировать последовательность чисел в заданном диапазоне.
Для всех скриптов будем использовать следующий набор переменных:
Можно найти много решений данной задачи, но многие из них можно свести к 7 способам. Сначала опишем каждое из решений, а затем сравним их производительность.
Решение 1: выводим цифры от 0 до 9
В данном решении используется обобщенное табличное выражение, в котором выводятся цифры от 0 до 9.
Перемножая эти списки необходимое число раз, можно получить набор записей, состоящий из требуемого количества элементов. Пронумеровав эти элементы по порядку оператором ROW_NUMBER, получаем список чисел.
Важно отметить, что добавление в запрос 15 соединений запроса x не сильно влияет на итоговую производительность. Это происходит из-за того, что оптимизатор определяет требуемое количество подзапросов, а из остальных берёт только по одной строке. Это можно увидеть в плане запроса.
Решение 2: используем функцию replicate
Второе решение основано на двух функциях:
replicate – для генерации строки, длина которой равна количеству элементов в списке, который требуется получить в результате
string_split – для разбиения строки на список
Решение 3: replicate + cross
Следующее решение является смесью из первых двух решений.
Сначала в x генерируется список, состоящий из 10 строк. Затем полученный список перемножается необходимое число раз, чтобы получился список из нужного количества записей.
Решение 4: recursive
Четвертый скрипт использует рекурсивный запрос для генерации последовательности чисел.
Рекурсивный запрос начинается с начального значения последовательности (@startnum) и использует оператор UNION ALL для добавления следующего значения в последовательность, пока не будет достигнуто конечное значение (@endnum).
Этот метод является простым в использовании, но может быть не так быстр, как другие методы, особенно для больших наборов данных.
Важно отметить, что для предотвращения входа в бесконечный цикл по умолчанию рекурсия выполняется не более 100 раз. Чтобы получить последовательность, состоящую из большего количества элементов, это ограничение было снято опцией maxrecursion 0.
Решение 5: while
Пятое решение может быть самое неоптимальное, но обойти его вниманием нельзя, потому что оно тоже решает задачу. Основано оно на использовании цикла WHILE, выполняемого до тех пор, пока временная таблица #numbers не заполнится.
Решение 6: spt_values
Следующее решение использует системную таблицу master.dbo.spt_values, соединяя её самой с собой нужное число раз.
Количество записей в этой таблице в разных базах различное, но, зная его, можно вычислить, сколько раз требуется выполнить cross join.
В моей таблице в этой таблице 2574 записи, поэтому соединив её саму с собой один раз, уже можно получить числа до 6 625 476. Соединив ещё раз, записей уже будет больше 17 миллиардов.
Конечно, вместо таблицы master.dbo.spt_values можно использовать и другие таблицы. Например, sys.all_objects. А можно использовать даже не системные таблицы, но при этом нужно быть уверенным, что количество записей в них не станет внезапно слишком маленьким.
Так же, как и в первом решении, заметим, что добавление в запрос 6 соединений таблицы master.dbo.spt_values тоже не сильно влияет на итоговую производительность. Покажем это вырезкой из плана запроса.
Решение 7: функция generate_series
И есть ещё один способ, о котором нужно сказать. В SQL Server версии от 2022 имеется встроенная функция для решения данной задачи – GENERATE_SERIES.
Сравнение производительности
Для сравнения скорости был использован скрипт, в котором 10 раз по очереди запускается каждый из тестов.
В результате получились следующие итоги в миллисекундах.
Расположив тесты в порядке увеличения скорости работы, получаем следующую диаграмму.
Хорошим подходом будет сделать функцию или хранимую процедуру для генерации чисел, в которую встроить выбранный способ. Мы для примера создания этих функции и хранимой процедуры будем использовать первый способ.
function dbo.generate_range
procedure dbo.get_range
Вывод
Таким образом, если у вас используется MS SQL SERVER версии от 2022, используйте для генерации встроенную функцию GENERATE_SERIES.
Для более старых версий выбирайте либо 1, либо 6 способы.