在SQL Server 2005上拆分日期范围到年-月行
我需要创建一个输出,其中我每年每月得到一行 假设数据集为:在SQL Server 2005上拆分日期范围到年-月行,sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,我需要创建一个输出,其中我每年每月得到一行 假设数据集为: id | dateStart | dateEnd 1 | 2015-01-01 00:00:00.000 | 2015-03-31 00:00:00.000 2 | 2014-07-01 00:00:00.000 | 2014-08-31 00:00:00.000 ... 我需要以下输出: id | year-month 1 | 2015-01 1 | 2015-02 1 |
id | dateStart | dateEnd
1 | 2015-01-01 00:00:00.000 | 2015-03-31 00:00:00.000
2 | 2014-07-01 00:00:00.000 | 2014-08-31 00:00:00.000
...
我需要以下输出:
id | year-month
1 | 2015-01
1 | 2015-02
1 | 2015-03
2 | 2014-07
2 | 2014-08
输出可以是任何数据类型,因为我可以稍后更改它。
也就是说,对于2015-01,以下是可以的,2015-01-01 00:00.0002015-01-012015012015年1月等
注意:我使用的是SQL Server 2005。这里有一个使用递归CTE的方法:
with CTE as (
select id, dateStart as dte, dateEnd
from t
union all
select id, dateadd(month, 1, dte), dateEnd
from CTE
where dateadd(month, 1, dte) < dateEnd
)
select id, dte
from CTE;
或
下面是一个使用递归CTE的方法:
with CTE as (
select id, dateStart as dte, dateEnd
from t
union all
select id, dateadd(month, 1, dte), dateEnd
from CTE
where dateadd(month, 1, dte) < dateEnd
)
select id, dte
from CTE;
或
生成tally Table只需确保有足够的行就可以了。计数将包含值0,1,2,…n。然后执行连接,条件是将此值作为月份添加到startDate,直到大于endDate:
生成tally Table只需确保有足够的行就可以了。计数将包含值0,1,2,…n。然后执行连接,条件是将此值作为月份添加到startDate,直到大于endDate:
在一个理想的世界中,您会有一个,那么您的查询将是:
SELECT t.id,
c.FirstDayOfMonth
FROM YourTable AS t
INNER JOIN dbo.Calendar c
ON c.FirstDayOfMonth >= t.DateStart
AND c.FirstDayOfMonth <= t.DateEnd
AND c.DayOfMonth = 1;
然后,您可以将其连接到原始表:
DECLARE @T TABLE (id INT, DateStart DATE, DateEnd DATE);
INSERT @T (ID, DateStart, DateEnd)
VALUES (1, '20150101', '20150331'), (2, '20140701', '20140831');
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N3.N) FROM N3)
SELECT t.ID,
[year-month] = DATEADD(MONTH, n.Number + DATEDIFF(MONTH, 0, t.DateStart), 0)
FROM @T AS t
INNER JOIN Numbers AS N
ON N.Number - 1 <= DATEDIFF(MONTH, t.DateStart, t.DateEnd);
在一个理想的世界中,您会有一个,那么您的查询将是:
SELECT t.id,
c.FirstDayOfMonth
FROM YourTable AS t
INNER JOIN dbo.Calendar c
ON c.FirstDayOfMonth >= t.DateStart
AND c.FirstDayOfMonth <= t.DateEnd
AND c.DayOfMonth = 1;
然后,您可以将其连接到原始表:
DECLARE @T TABLE (id INT, DateStart DATE, DateEnd DATE);
INSERT @T (ID, DateStart, DateEnd)
VALUES (1, '20150101', '20150331'), (2, '20140701', '20140831');
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N3.N) FROM N3)
SELECT t.ID,
[year-month] = DATEADD(MONTH, n.Number + DATEDIFF(MONTH, 0, t.DateStart), 0)
FROM @T AS t
INNER JOIN Numbers AS N
ON N.Number - 1 <= DATEDIFF(MONTH, t.DateStart, t.DateEnd);
@MartinCarlsson,如果间隔大于100个月,您可能需要添加max recursion选项。需要注意的一点是,递归CTE只是一段时间循环的语法糖,根本不可伸缩。是的,在这种情况下,这并不重要,因为迭代次数不多,但是下一个阅读本文并应用相同逻辑生成从1到1000000的数字列表的人怎么办,然后写一个问题,想知道为什么他们的查询需要几天。虽然社区的目标是回答问题并提供有效的解决方案,但我相信我们也应该推广最佳实践。谢谢。我的源表只会增长到几千个月,最多48个月。我正在添加MAXRECURSION作为一个安全阀,以防源表中出现错误。。。从CTE选项MAXRECURSION 100中选择id、dte@MartinCarlsson,实际上100是默认值,您没有用该语句更改任何内容…@MartinCarlsson,如果间隔大于100个月,您可能需要添加max recursion选项。需要注意的一点是,递归CTE只是一段时间循环的语法糖,根本不可伸缩。是的,在这种情况下,这并不重要,因为迭代次数不多,但是下一个阅读本文并应用相同逻辑生成从1到1000000的数字列表的人怎么办,然后写一个问题,想知道为什么他们的查询需要几天。虽然社区的目标是回答问题并提供有效的解决方案,但我相信我们也应该推广最佳实践。谢谢。我的源表只会增长到几千个月,最多48个月。我正在添加MAXRECURSION作为一个安全阀,以防源表中出现错误。。。从CTE选项MAXRECURSION 100中选择id、dte@MartinCarlsson,实际上100是默认值,你没有用那句话改变任何东西。。。
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N3.N) FROM N3)
SELECT * FROM Numbers;
DECLARE @T TABLE (id INT, DateStart DATE, DateEnd DATE);
INSERT @T (ID, DateStart, DateEnd)
VALUES (1, '20150101', '20150331'), (2, '20140701', '20140831');
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N3.N) FROM N3)
SELECT t.ID,
[year-month] = DATEADD(MONTH, n.Number + DATEDIFF(MONTH, 0, t.DateStart), 0)
FROM @T AS t
INNER JOIN Numbers AS N
ON N.Number - 1 <= DATEDIFF(MONTH, t.DateStart, t.DateEnd);