生成表示完整日历月的日历 Table
Generate Calendar Table Representing a Full Calendar Month
我正在尝试创建一个日历 table 代表日历中的完整月份,包括上个月和下个月的重叠天数。
我已经非常接近从另一个 site/post 改编的以下脚本:
DECLARE
@year INT = 2019
, @month INT = 1
;
DECLARE
@firstDOM DATETIME
, @lastDOM DATETIME
, @firstDay VARCHAR(10)
, @weekid TINYINT
, @opDate DATE
, @firstDOW DATE
, @lastDOW DATE
;
-- get mid-month operation date
SELECT @opDate = CONVERT(DATE, (CAST(@year AS CHAR(4)) + '-' + CAST(@month AS VARCHAR(2)) + '-15'))
;
-- get first day of month
SET @firstDOM = DATEADD(mm, DATEDIFF(mm, 0, @opDate), 0)
;
-- get last day of month
SET @lastDOM = DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, @opDate) + 1, 0))
;
-- get first day of week
SET @firstDOW = @firstDOM - DATEPART(dw, @firstDOM) + 1
;
-- get last day of week
SET @lastDOW = @lastDOM + (7 - DATEPART(dw, @lastDOM))
;
-- get first day name
SELECT @firstDay = DATENAME(WEEKDAY, @firstDOW)
;
---------- Recursive CTE to get Days and Dates for the month
;WITH cte_cal ([Date], [Day], [WeekID])
as (
SELECT
@firstDOW
, @firstDay
, DATEPART(WW, @firstDOW) AS WeekID
UNION ALL
SELECT
DATEADD(DD, 1, [Date])
, CAST(DATENAME(WEEKDAY, DATEADD(DD, 1, [Date])) AS VARCHAR(10))
, DATEPART(WW, DATEADD(DD, 1, [Date]))AS WeekID
FROM
cte_cal
WHERE
[Date] < @lastDOW
)
------- Use Pivot to display the result in calender format
SELECT
[WeekID], [Sunday] , [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday]
FROM (
SELECT
[WeekID], [Date], [DAY]
FROM
cte_cal
) pvt
PIVOT (
MAX([Date]) FOR [Day] IN ([Sunday], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday])
) Pvttab
;
但是,1 月和 12 月的行为不正确,如下图所示,其中 2019 年的每个月都会被触发:
Calendar table output with incorrect January and December values
如有任何帮助,我们将不胜感激。
谢谢!
对您的查询进行了一些小的更改以解决问题周。可能需要做一些 QA 以确保它在所有情况下都能正常工作。这是 SQL Fiddle
特别是在 CTE(两个联合查询)中,我添加了一个 CASE
语句来强制将周数设为 1 或 53:
...
, CASE WHEN YEAR(@firstDOW) = @year + 1 THEN 52
WHEN YEAR(@firstDOW) = @year - 1 THEN 1
ELSE DATEPART(WW, @firstDOW) END AS WeekID
UNION ALL
...
, CASE WHEN YEAR(DATEADD(DD, 1, [Date])) = @year + 1 THEN 53
WHEN YEAR(DATEADD(DD, 1, [Date])) = @year - 1 THEN 1
ELSE DATEPART(WW, DATEADD(DD, 1, [Date])) END AS WeekID
...
总计:
DECLARE
@year INT = 2020
, @month INT = 12
, @firstDOM DATETIME
, @lastDOM DATETIME
, @firstDay VARCHAR(10)
, @weekid TINYINT
, @opDate DATE
, @firstDOW DATE
, @lastDOW DATE
-- get mid-month operation date
SELECT @opDate = CONVERT(DATE, (CAST(@year AS CHAR(4)) + '-' + CAST(@month AS VARCHAR(2)) + '-15'))
-- get first day of month
SET @firstDOM = DATEADD(mm, DATEDIFF(mm, 0, @opDate), 0)
-- get last day of month
SET @lastDOM = DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, @opDate) + 1, 0))
-- get first day of week
SET @firstDOW = @firstDOM - DATEPART(dw, @firstDOM) + 1
-- get last day of week
SET @lastDOW = @lastDOM + (7 - DATEPART(dw, @lastDOM))
-- get first day name
SELECT @firstDay = DATENAME(WEEKDAY, @firstDOW)
---------- Recursive CTE to get Days and Dates for the month
;WITH cte_cal ([Date], [Day], [WeekID])
as (
SELECT
@firstDOW
, @firstDay
, CASE WHEN YEAR(@firstDOW) = @year + 1 THEN 52
WHEN YEAR(@firstDOW) = @year - 1 THEN 1
ELSE DATEPART(WW, @firstDOW) END AS WeekID
UNION ALL
SELECT
DATEADD(DD, 1, [Date])
, CAST(DATENAME(WEEKDAY, DATEADD(DD, 1, [Date])) AS VARCHAR(10))
, CASE WHEN YEAR(DATEADD(DD, 1, [Date])) = @year + 1 THEN 53
WHEN YEAR(DATEADD(DD, 1, [Date])) = @year - 1 THEN 1
ELSE DATEPART(WW, DATEADD(DD, 1, [Date])) END AS WeekID
FROM
cte_cal
WHERE
[Date] < @lastDOW
)
------- Use Pivot to display the result in calender format
SELECT
[WeekID], [Sunday], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday],
[Saturday]
FROM (
SELECT
[WeekID], [Date], [DAY]
FROM
cte_cal
) pvt
PIVOT (
MAX([Date]) FOR [Day] IN ([Sunday], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday])
) Pvttab
;
还有一个选择
例子
Declare @Date1 date = '2020-02-01'
Select RowNr,[Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]
From (
Select D
,DOW=left(datename(WEEKDAY,d),3)
,RowNr = sum(Flg) over (order by D)
From (
Select D,Flg=case when datename(WEEKDAY,d)= 'Sunday' then 1 else 0 end
From (Select Top (42) D=DateAdd(DAY,-7+Row_Number() Over (Order By (Select Null)),@Date1) From master..spt_values n1 ) A
) A
) src
Pivot (max(d) for DOW in ([Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]) )pvg
Where [Sun] is not null
and [Sat] is not null
Returns
当我注意到电流T-Sql时,我开始怀疑它是否真的需要那么多变量。
所以我有点开始从头开始涂鸦。
结果适用于 DATEFIRST 7
(周日开始)。
递归 CTE 只展开当月的日期。
但它会针对一周的第一个日期进行更正,即使该周是从前一年开始的。结束日期类似。
因为大多数与日期相关的信息都可以从日期中导出。
DECLARE @year INT, @month INT;
SET @year = YEAR(GetDate());
SET @month = 1;
SET DATEFIRST 7;
WITH CTE_DATES AS
(
SELECT
DATEADD(DAY, 1-DATEPART(WEEKDAY, DATEFROMPARTS(@year, @month, 1)), DATEFROMPARTS(@year, @month, 1)) AS [Date],
DATEFROMPARTS(@year, @month, 1) AS initDate,
DATEADD(dd, 7-(DATEPART(dw, EOMONTH(DATEFROMPARTS(@year, @month, 1)) )), EOMONTH(DATEFROMPARTS(@year, @month, 1))) AS endDate
UNION ALL
SELECT DATEADD(day,1, [Date]), initDate, endDate
FROM CTE_DATES
WHERE [Date] < endDate
),
CTE_CALENDAR AS
(
SELECT
(CASE
WHEN MONTH(initDate) = 1 AND DATEPART(WW, [Date]) > 50
THEN 1
WHEN YEAR([Date]) > YEAR(initDate)
THEN DATEPART(WW, EOMONTH(initDate))
ELSE DATEPART(WW, [Date])
END) AS [WeekID],
DATENAME(WEEKDAY, [Date]) AS [Day],
CONVERT(VARCHAR(10),[Date],23) AS [Date]
FROM CTE_DATES
)
SELECT *
FROM CTE_CALENDAR
PIVOT
(
MAX([Date])
FOR [Day] IN ([Sunday] , [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday])
) Pvt
ORDER BY [WeekID];
rextester 测试here
我正在尝试创建一个日历 table 代表日历中的完整月份,包括上个月和下个月的重叠天数。
我已经非常接近从另一个 site/post 改编的以下脚本:
DECLARE
@year INT = 2019
, @month INT = 1
;
DECLARE
@firstDOM DATETIME
, @lastDOM DATETIME
, @firstDay VARCHAR(10)
, @weekid TINYINT
, @opDate DATE
, @firstDOW DATE
, @lastDOW DATE
;
-- get mid-month operation date
SELECT @opDate = CONVERT(DATE, (CAST(@year AS CHAR(4)) + '-' + CAST(@month AS VARCHAR(2)) + '-15'))
;
-- get first day of month
SET @firstDOM = DATEADD(mm, DATEDIFF(mm, 0, @opDate), 0)
;
-- get last day of month
SET @lastDOM = DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, @opDate) + 1, 0))
;
-- get first day of week
SET @firstDOW = @firstDOM - DATEPART(dw, @firstDOM) + 1
;
-- get last day of week
SET @lastDOW = @lastDOM + (7 - DATEPART(dw, @lastDOM))
;
-- get first day name
SELECT @firstDay = DATENAME(WEEKDAY, @firstDOW)
;
---------- Recursive CTE to get Days and Dates for the month
;WITH cte_cal ([Date], [Day], [WeekID])
as (
SELECT
@firstDOW
, @firstDay
, DATEPART(WW, @firstDOW) AS WeekID
UNION ALL
SELECT
DATEADD(DD, 1, [Date])
, CAST(DATENAME(WEEKDAY, DATEADD(DD, 1, [Date])) AS VARCHAR(10))
, DATEPART(WW, DATEADD(DD, 1, [Date]))AS WeekID
FROM
cte_cal
WHERE
[Date] < @lastDOW
)
------- Use Pivot to display the result in calender format
SELECT
[WeekID], [Sunday] , [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday]
FROM (
SELECT
[WeekID], [Date], [DAY]
FROM
cte_cal
) pvt
PIVOT (
MAX([Date]) FOR [Day] IN ([Sunday], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday])
) Pvttab
;
但是,1 月和 12 月的行为不正确,如下图所示,其中 2019 年的每个月都会被触发:
Calendar table output with incorrect January and December values
如有任何帮助,我们将不胜感激。
谢谢!
对您的查询进行了一些小的更改以解决问题周。可能需要做一些 QA 以确保它在所有情况下都能正常工作。这是 SQL Fiddle
特别是在 CTE(两个联合查询)中,我添加了一个 CASE
语句来强制将周数设为 1 或 53:
...
, CASE WHEN YEAR(@firstDOW) = @year + 1 THEN 52
WHEN YEAR(@firstDOW) = @year - 1 THEN 1
ELSE DATEPART(WW, @firstDOW) END AS WeekID
UNION ALL
...
, CASE WHEN YEAR(DATEADD(DD, 1, [Date])) = @year + 1 THEN 53
WHEN YEAR(DATEADD(DD, 1, [Date])) = @year - 1 THEN 1
ELSE DATEPART(WW, DATEADD(DD, 1, [Date])) END AS WeekID
...
总计:
DECLARE
@year INT = 2020
, @month INT = 12
, @firstDOM DATETIME
, @lastDOM DATETIME
, @firstDay VARCHAR(10)
, @weekid TINYINT
, @opDate DATE
, @firstDOW DATE
, @lastDOW DATE
-- get mid-month operation date
SELECT @opDate = CONVERT(DATE, (CAST(@year AS CHAR(4)) + '-' + CAST(@month AS VARCHAR(2)) + '-15'))
-- get first day of month
SET @firstDOM = DATEADD(mm, DATEDIFF(mm, 0, @opDate), 0)
-- get last day of month
SET @lastDOM = DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, @opDate) + 1, 0))
-- get first day of week
SET @firstDOW = @firstDOM - DATEPART(dw, @firstDOM) + 1
-- get last day of week
SET @lastDOW = @lastDOM + (7 - DATEPART(dw, @lastDOM))
-- get first day name
SELECT @firstDay = DATENAME(WEEKDAY, @firstDOW)
---------- Recursive CTE to get Days and Dates for the month
;WITH cte_cal ([Date], [Day], [WeekID])
as (
SELECT
@firstDOW
, @firstDay
, CASE WHEN YEAR(@firstDOW) = @year + 1 THEN 52
WHEN YEAR(@firstDOW) = @year - 1 THEN 1
ELSE DATEPART(WW, @firstDOW) END AS WeekID
UNION ALL
SELECT
DATEADD(DD, 1, [Date])
, CAST(DATENAME(WEEKDAY, DATEADD(DD, 1, [Date])) AS VARCHAR(10))
, CASE WHEN YEAR(DATEADD(DD, 1, [Date])) = @year + 1 THEN 53
WHEN YEAR(DATEADD(DD, 1, [Date])) = @year - 1 THEN 1
ELSE DATEPART(WW, DATEADD(DD, 1, [Date])) END AS WeekID
FROM
cte_cal
WHERE
[Date] < @lastDOW
)
------- Use Pivot to display the result in calender format
SELECT
[WeekID], [Sunday], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday],
[Saturday]
FROM (
SELECT
[WeekID], [Date], [DAY]
FROM
cte_cal
) pvt
PIVOT (
MAX([Date]) FOR [Day] IN ([Sunday], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday])
) Pvttab
;
还有一个选择
例子
Declare @Date1 date = '2020-02-01'
Select RowNr,[Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]
From (
Select D
,DOW=left(datename(WEEKDAY,d),3)
,RowNr = sum(Flg) over (order by D)
From (
Select D,Flg=case when datename(WEEKDAY,d)= 'Sunday' then 1 else 0 end
From (Select Top (42) D=DateAdd(DAY,-7+Row_Number() Over (Order By (Select Null)),@Date1) From master..spt_values n1 ) A
) A
) src
Pivot (max(d) for DOW in ([Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]) )pvg
Where [Sun] is not null
and [Sat] is not null
Returns
当我注意到电流T-Sql时,我开始怀疑它是否真的需要那么多变量。
所以我有点开始从头开始涂鸦。
结果适用于 DATEFIRST 7
(周日开始)。
递归 CTE 只展开当月的日期。
但它会针对一周的第一个日期进行更正,即使该周是从前一年开始的。结束日期类似。
因为大多数与日期相关的信息都可以从日期中导出。
DECLARE @year INT, @month INT;
SET @year = YEAR(GetDate());
SET @month = 1;
SET DATEFIRST 7;
WITH CTE_DATES AS
(
SELECT
DATEADD(DAY, 1-DATEPART(WEEKDAY, DATEFROMPARTS(@year, @month, 1)), DATEFROMPARTS(@year, @month, 1)) AS [Date],
DATEFROMPARTS(@year, @month, 1) AS initDate,
DATEADD(dd, 7-(DATEPART(dw, EOMONTH(DATEFROMPARTS(@year, @month, 1)) )), EOMONTH(DATEFROMPARTS(@year, @month, 1))) AS endDate
UNION ALL
SELECT DATEADD(day,1, [Date]), initDate, endDate
FROM CTE_DATES
WHERE [Date] < endDate
),
CTE_CALENDAR AS
(
SELECT
(CASE
WHEN MONTH(initDate) = 1 AND DATEPART(WW, [Date]) > 50
THEN 1
WHEN YEAR([Date]) > YEAR(initDate)
THEN DATEPART(WW, EOMONTH(initDate))
ELSE DATEPART(WW, [Date])
END) AS [WeekID],
DATENAME(WEEKDAY, [Date]) AS [Day],
CONVERT(VARCHAR(10),[Date],23) AS [Date]
FROM CTE_DATES
)
SELECT *
FROM CTE_CALENDAR
PIVOT
(
MAX([Date])
FOR [Day] IN ([Sunday] , [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday])
) Pvt
ORDER BY [WeekID];
rextester 测试here