#sql #sql-server
#sql #sql-server
Вопрос:
У меня есть таблица с типами int, datetime, datetime:
id start date end date
-- ---------- ----------
1 2019-04-02 2020-09-17
2 2019-08-10 2020-08-10
Вот создать / вставить:
CREATE TABLE dbo.something
(
id int,
[start date] datetime,
[end date] datetime
);
INSERT dbo.something(id,[start date],[end date])
VALUES(1,'20190402','20200917'),(2,'20190810','20200810');
Что такое SQL-запрос, который может выдавать эти результаты:
id Year Quarter
-- ---- ----------
1 2019 2
1 2019 3
1 2019 4
1 2020 1
1 2020 2
1 2020 3
2 2019 3
2 2019 4
2 2020 1
2 2020 2
2 2020 3
Ответ №1:
Просто используйте рекурсивный CTE. Эта версия переключается на подсчет кварталов с 0 года:
with cte as (
select id,
year(start_date) * 4 datepart(quarter, start_date) - 1 as yyyyq,
year(end_date) * 4 datepart(quarter, end_date) - 1 as end_yyyyq
from t
union all
select id, yyyyq 1, end_yyyyq
from cte
where yyyyq < end_yyyyq
)
select id, yyyyq / 4 as year, (yyyyq % 4) 1 as quarter
from cte;
Вот скрипка db<> .
Комментарии:
1. Это именно то, что я ищу! Спасибо!!
Ответ №2:
Если вы не можете создать другую справочную таблицу / etc, вы можете использовать DATEDIFF (и DATEPART) с использованием quarters, а затем выполнить простую арифметику даты.
Приведенная ниже логика заключается в том, чтобы просто найти для каждой начальной даты первый квартал, а затем количество дополнительных кварталов, чтобы достичь максимума. Затем выберите, где дополнительные кварталы добавляются к начальной дате, чтобы получить каждый квартал.
Самая сложная часть запроса для понимания imo — это WITH numberlist
раздел — все, что он делает, это генерирует серию целых чисел от 0 до максимального количества кварталов разницы. Если у вас уже есть таблица numbers, вы можете использовать ее вместо этого.
Часть ключевого кода приведена ниже, а вот полный DB_Fiddle с некоторыми дополнительными тестовыми данными.
CREATE TABLE #yourtable (id int, startdate date, enddate date)
INSERT INTO #yourtable (id, startdate, enddate) VALUES
(1, '2019-04-02', '2020-09-17'),
(2, '2019-08-10', '2020-08-20')
; WITH number_list AS
-- list of ints from 0 to maximum number of quarters
(SELECT n
FROM (SELECT ones.n 10*tens.n AS n
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n)
) AS a
WHERE n <= (SELECT MAX(DATEDIFF(quarter,startdate,enddate)) FROM #yourtable)
)
SELECT id,
YEAR(DATEADD(quarter, number_list.n, startdate)) AS [Year],
DATEPART(quarter, DATEADD(quarter, number_list.n, startdate)) AS [Quarter]
FROM (SELECT id, startdate, DATEDIFF(quarter,startdate,enddate) AS num_additional_quarters FROM #yourtable) yt
CROSS JOIN number_list
WHERE number_list.n <= yt.num_additional_quarters
DROP TABLE #yourtable
Комментарии:
1.
'2019-04-02'
это плохой буквальный формат, проверьте, что он возвращает, скажем,SET LANGUAGE GERMAN;
. Гораздо безопаснее использовать'20190402'
, тогда SQL Server не сможет неправильно истолковать.2. Боже, это ужасно @AaronBertrand — спасибо, что указали на это! Я думал, что предпочтительным форматом является использование тире, но я узнал кое-что новое. Спасибо!
3. Это точно. Я упоминал об этом в большем количестве статей и презентаций, чем могу вспомнить.
Ответ №3:
Сначала создайте таблицу измерения даты, содержащую дату, соответствующий квартал и год. Затем используйте приведенный ниже запрос, чтобы получить результат. Настройте имя столбца и таблицы в соответствии с вашей схемой.
with q_date as
(
select 1 as id, '2019-04-02' :: date as start_date, '2020-09-17' :: date as end_date
UNION ALL
select 2 as id, '2019-08-10' :: date as start_date, '2020-08-10' :: date as end_date
)
select qd.id, dd.calendar_year, dd.calendar_quarter_number
from dim_date dd, q_date qd
where dd.date_dmk between qd.start_date and qd.end_date
group by qd.id, dd.calendar_year, dd.calendar_quarter_number
order by qd.id, dd.calendar_year, dd.calendar_quarter_number;
Комментарии:
1. Группировать по 1,2,3 ??
2. это ленивый ярлык для замены псевдонимов столбцов. Вы всегда можете использовать имена столбцов в group by .
3. Хорошо известно, но вы пробовали ГРУППИРОВАТЬ ПО в SQL Server?
4. Это было бы ужасной идеей, даже если бы это было законно в SQL Server, ИМХО. sqlblog.org/2009/10/06/bad-habits-to-kick-order-by-ordinal
5. Я подумал, что вы могли бы также исправить
'2019-04-02' :: date
, что не является законным в SQL Server.