GENERATE_SERIES в SQL Server
В SQL Server 2022 появилась функция генерации числовой последовательности, GENERATE_SERIES. Подобная функция имеется в некоторых других СУБД, в частности, в PostgreSQL. Вот, например, как с помощью этой функции решалась следующая задача из учебника.
Вывести последовательность дат между датами первого и последнего полета пассажира с id_psg = 5.
Решение:
Вывести последовательность дат между датами первого и последнего полета пассажира с id_psg = 5.
Решение:
SELECT generate_series(MIN(date), MAX(date), '1 day')
FROM pass_in_trip
WHERE id_psg = 5;Результат можно посмотреть, выполнив запрос в консоли учебника или на сайте sql-ex.ru (не забудьте выбрать PostgreSQL). Вот скриншот с консоли:

Несмотря на схожесть, реализация функции GENERATE_SERIES в SQL Server существенно отличается от реализации в PostgreSQL. Начать с того, что генерируется только числовая последовательность, и все аргументы функции относятся к числовым типам. Кроме того, поскольку функция создает одностолбцовую таблицу, она естественно используется в предложении FROM. Вот пример использования функции GENERATE_SERIES для создание ряда целых чисел в диапазоне от 1 до 50 с шагом приращения в 5, заимствованный из документации Microsoft:
SELECT value
FROM GENERATE_SERIES(1, 50, 5);Чтобы получить решение нашей задачи в диалекте SQL Server, первое, что приходит в голову, - посчитать количество дней в интервале между MIN(date) и MAX(date) (назовем его cnt), а затем использовать это число в вызове функции:
GENERATE_SERIES(1, cnt, 1)Т.е. можно предложить такое решение:
WITH gs AS
(SELECT MIN(date) mind, DATEDIFF(day,MIN(date), MAX(date)) cnt
FROM pass_in_trip WHERE id_psg = 5)
SELECT DATEADD( DAY, value, mind) _date_
FROM gs CROSS APPLY GENERATE_SERIES(0, gs.cnt, 1);Кстати, в качестве параметра функции GENERATE_SERIES можно использовать непосредственно подзапрос. Тогда я прихожу к такому решения задачи:
SELECT DATEADD( DAY, value, (SELECT MIN(date) FROM pass_in_trip WHERE id_psg = 5)) _date_
FROM GENERATE_SERIES(0, (SELECT DATEDIFF(day, MIN(date), MAX(date)) FROM pass_in_trip WHERE id_psg = 5), 1);Тут мне пришлось дважды использовать фактически один и тот же подзапрос, тогда как в первом решении я избегаю этого с помощью CTE и CROSS APPLY. Можно предложить вариант с CTE и без CROSS APPLY:
WITH gs AS
(SELECT MIN(date) mind, DATEDIFF(day,MIN(date), MAX(date)) cnt
FROM pass_in_trip WHERE id_psg = 5)
SELECT DATEADD( DAY, value, (SELECT mind FROM gs)) _date_
FROM GENERATE_SERIES(0, (SELECT gs.cnt FROM gs), 1);Так или иначе, но на мой взгляд решение в PostgreSQL выглядит более элегантным.
Важно. Для использования функции GENERATE_SERIES в SQL Server уровень совместимости базы данных должен быть не менее 160.
Ссылки по теме
1. Генерация числовой последовательности
2. Общие табличные выражения
3. Работа с данными временных рядов в SQL Server 2022 и Azure SQL
4. Мои любимые улучшения T-SQL в SQL Server 2022
5. CROSS APPLY / OUTER APPLY
Trackbacks
The author does not allow comments to this entry
Comments
Display comments as Linear | Threaded