从给定的日期时间为每年的月份生成行
Generate row for month of each year from given datetime
我使用以下查询根据名为 'InsertDate' 的 table 字段生成行:
;WITH listOfMonth(MonthNumber,ContractId )
AS
(
select
DATEPART(month, StartDate) as m,Id as ContractId from tblContract where Id = 6674
union all
select MonthNumber + 1 as MonthNumber ,ContractId
from listOfMonth
where MonthNumber < DATEPART(month, GETDATE())
)
select * from listOfMonth
例如,如果 InsertDate
是 4/19/2021,我有以下输出:
ID
MonthNumber
ContractId
1
4
6674
2
5
6674
3
6
6674
4
7
6674
5
8
6674
6
9
6674
但我还需要根据年份生成行,所以我更改了查询:
;WITH listOfMonth(YearNumber,MonthNumber,ContracId)
AS
(
select
DATEPART(Year, StartDate) as YearNumber,DATEPART(month, StartDate) as m,Id as ContracId from tblContract where Id = 6674
union all
select YearNumber, MonthNumber + 1 as MonthNumber ,ContracId
from listOfMonth
where MonthNumber < DATEPART(month, GETDATE())
)
select * from listOfMonth
此查询生成年份列,但仅针对 InsertDate 的当前年份,
例如,如果 InsertDate 是 4/19/2020,它只生成 2020 年的列,但我想为 2020 年和 2021 年生成列
我使用 SQL Server 2008 R2
最终输出应该是这样的:
ID
MonthNumber
ContractId
YearNumber
1
4
6674
2020
2
5
6674
2020
3
6
6674
2020
4
7
6674
2020
5
8
6674
2020
6
9
6674
2020
7
10
6674
2020
8
11
6674
2020
9
12
6674
2020
10
1
6674
2021
11
2
6674
2021
12
3
6674
2021
13
4
6674
2021
14
5
6674
2021
15
6
6674
2021
16
7
6674
2021
16
8
6674
2021
16
9
6674
2021
我会将月份表示为 日期:
with listOfMonth AS(
select datefromparts(year(startdate), month(startdate), 1) as yyyymm,
Id as ContractId
from tblContract
where Id = 6674
union all
select dateadd(month, 1, yyyymm), ContractId
from listOfMonth
where yyyymm < datefromparts(year(getdate()), month(getdate()), 1)
)
select *
from listOfMonth
option maxrecursion (0);
如果您想在单独的列中显示年份和月份,请在外部查询中执行此操作:
select year(yyyymm) as year, month(yyyymm) as month
SQL 服务器中的递归 CTE 是循环数据的缓慢方式(这就是限制设置得如此低的原因,即“最大递归 100 已用尽”)。生成行的快速方法是使用“数字 table”(包含序列作为行的 table)或 function(又名“计数函数”)。这使用称为 dbo.fnTally 的计数函数。可以用数字 table 代替计数功能。
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Jeff Moden Script on SSC: https://www.sqlservercentral.com/scripts/create-a-tally-function-fntally
**********************************************************************************************************************/
(@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
H2(N) AS ( SELECT 1
FROM (VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)V(N)) --16^2 or 256 rows
, H4(N) AS (SELECT 1 FROM H2 a, H2 b) --16^4 or 65,536 rows
, H8(N) AS (SELECT 1 FROM H4 a, H4 b) --16^8 or 4,294,967,296 rows
SELECT N = 0 WHERE @ZeroOrOne = 0 UNION ALL
SELECT TOP(@MaxN)
N = ROW_NUMBER() OVER (ORDER BY N)
FROM H8
;
select year(calc.dt) YearNumber,
month(calc.dt) MonthNumber,
c.ContractId
from (values (cast('20190101' as date), 123),
(cast('20201201' as date), 456)) c(StartDate, ContractId)
cross apply dbo.fnTally(0, datediff(month, StartDate, getdate())) fn
cross apply (values (dateadd(month, fn.n, StartDate))) calc(dt)
order by c.ContractId, YearNumber, MonthNumber;
YearNumber MonthNumber ContractId
2019 1 123
2019 2 123
...
2019 12 123
2020 1 123
...
2021 9 123
2020 12 456
2021 1 456
...
2021 9 456
我使用以下查询根据名为 'InsertDate' 的 table 字段生成行:
;WITH listOfMonth(MonthNumber,ContractId )
AS
(
select
DATEPART(month, StartDate) as m,Id as ContractId from tblContract where Id = 6674
union all
select MonthNumber + 1 as MonthNumber ,ContractId
from listOfMonth
where MonthNumber < DATEPART(month, GETDATE())
)
select * from listOfMonth
例如,如果 InsertDate
是 4/19/2021,我有以下输出:
ID | MonthNumber | ContractId |
---|---|---|
1 | 4 | 6674 |
2 | 5 | 6674 |
3 | 6 | 6674 |
4 | 7 | 6674 |
5 | 8 | 6674 |
6 | 9 | 6674 |
但我还需要根据年份生成行,所以我更改了查询:
;WITH listOfMonth(YearNumber,MonthNumber,ContracId)
AS
(
select
DATEPART(Year, StartDate) as YearNumber,DATEPART(month, StartDate) as m,Id as ContracId from tblContract where Id = 6674
union all
select YearNumber, MonthNumber + 1 as MonthNumber ,ContracId
from listOfMonth
where MonthNumber < DATEPART(month, GETDATE())
)
select * from listOfMonth
此查询生成年份列,但仅针对 InsertDate 的当前年份, 例如,如果 InsertDate 是 4/19/2020,它只生成 2020 年的列,但我想为 2020 年和 2021 年生成列 我使用 SQL Server 2008 R2 最终输出应该是这样的:
ID | MonthNumber | ContractId | YearNumber |
---|---|---|---|
1 | 4 | 6674 | 2020 |
2 | 5 | 6674 | 2020 |
3 | 6 | 6674 | 2020 |
4 | 7 | 6674 | 2020 |
5 | 8 | 6674 | 2020 |
6 | 9 | 6674 | 2020 |
7 | 10 | 6674 | 2020 |
8 | 11 | 6674 | 2020 |
9 | 12 | 6674 | 2020 |
10 | 1 | 6674 | 2021 |
11 | 2 | 6674 | 2021 |
12 | 3 | 6674 | 2021 |
13 | 4 | 6674 | 2021 |
14 | 5 | 6674 | 2021 |
15 | 6 | 6674 | 2021 |
16 | 7 | 6674 | 2021 |
16 | 8 | 6674 | 2021 |
16 | 9 | 6674 | 2021 |
我会将月份表示为 日期:
with listOfMonth AS(
select datefromparts(year(startdate), month(startdate), 1) as yyyymm,
Id as ContractId
from tblContract
where Id = 6674
union all
select dateadd(month, 1, yyyymm), ContractId
from listOfMonth
where yyyymm < datefromparts(year(getdate()), month(getdate()), 1)
)
select *
from listOfMonth
option maxrecursion (0);
如果您想在单独的列中显示年份和月份,请在外部查询中执行此操作:
select year(yyyymm) as year, month(yyyymm) as month
SQL 服务器中的递归 CTE 是循环数据的缓慢方式(这就是限制设置得如此低的原因,即“最大递归 100 已用尽”)。生成行的快速方法是使用“数字 table”(包含序列作为行的 table)或 function(又名“计数函数”)。这使用称为 dbo.fnTally 的计数函数。可以用数字 table 代替计数功能。
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Jeff Moden Script on SSC: https://www.sqlservercentral.com/scripts/create-a-tally-function-fntally
**********************************************************************************************************************/
(@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
H2(N) AS ( SELECT 1
FROM (VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)V(N)) --16^2 or 256 rows
, H4(N) AS (SELECT 1 FROM H2 a, H2 b) --16^4 or 65,536 rows
, H8(N) AS (SELECT 1 FROM H4 a, H4 b) --16^8 or 4,294,967,296 rows
SELECT N = 0 WHERE @ZeroOrOne = 0 UNION ALL
SELECT TOP(@MaxN)
N = ROW_NUMBER() OVER (ORDER BY N)
FROM H8
;
select year(calc.dt) YearNumber,
month(calc.dt) MonthNumber,
c.ContractId
from (values (cast('20190101' as date), 123),
(cast('20201201' as date), 456)) c(StartDate, ContractId)
cross apply dbo.fnTally(0, datediff(month, StartDate, getdate())) fn
cross apply (values (dateadd(month, fn.n, StartDate))) calc(dt)
order by c.ContractId, YearNumber, MonthNumber;
YearNumber MonthNumber ContractId
2019 1 123
2019 2 123
...
2019 12 123
2020 1 123
...
2021 9 123
2020 12 456
2021 1 456
...
2021 9 456