在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);