(SQL Server 2008 及更高版本):我需要更新 dimDate
表以包含以下列:
- 本月最后一个星期日:
- 本月最后一个星期一:
- 本月最后一个星期二:
- 本月最后一个星期三:
- 本月最后一个星期四:
- 本月最后一个星期五:
- 本月最后一个星期六:
别误会我的意思;通过以下代码在 SQL 中定位该月的最后一天非常简单:
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime))) AS [LastDayOfTheMonth]
但我在互联网上找不到任何地方,而且我承认没有花费超过 2 小时的搜索时间,其中人们公开展示了如何识别该月的最后一个(插入日期名称)。
因此,我解决了这个方程并将其发布在这里,以防它对其他人有用,或者,如果有人有一个我可以使用的更简单的方法,但根本看不到它。
dimDates
表通过预填充的 dimNumbers
表进行填充:
IF OBJECT_ID('dbo.dimNumbers') IS NOT NULL
DROP TABLE dbo.dimNumbers;
DECLARE @UpperBound INT = 1000000;
;WITH cteN(Number) AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
FROM
sys.all_columns AS s1
CROSS JOIN
sys.all_columns AS s2
)
SELECT [Number]
INTO ref.dimNumbers
FROM cteN
WHERE [Number] <= @UpperBound;
CREATE UNIQUE CLUSTERED INDEX CIX_dimNumbers ON ref.dimNumbers([Number]);
然后通过以下方式填充暗淡的日期表。是的,我很懒,希望 SQL 执行所有可能的计算。
DECLARE @YearsToPopulate INT = 130;
-- Use the Magic of SQL to identify 1 Jan and then 31st December at the various edges of the required date time frames.
DECLARE @StartDate DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,-@YearsToPopulate,GETDATE())), 0);
DECLARE @EndDate DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,@YearsToPopulate,GETDATE())) + 1, -1);
DECLARE @RecordsToCreate INT = DATEDIFF(dd,@StartDate,@EndDate);
;WITH MyFullDateRange AS
(
SELECT TOP (@RecordsToCreate)
CAST(DATEADD(dd, Number, @StartDate) AS DATE) AS DayInTime
FROM
ref.[dimNumbers]
)
SELECT
--Insert Formulas here, using [DayInTime] as the Variable
-- The Formulas I have used here are not the topic of this discussion.
FROM
MyFullDateRange
因此,我花了几个小时来证明,但我最终想出了一个简单的、可重复的模式来发现“本月的最后一个(插入日期名称)”。
注意:将以下内容放入上述 SELECT
语句中。
--Sunday [DayOfWeek] = 1.. Need to convert 1 to 0 <-> N + (7 - 1) % 7
DATEADD(DD,
- ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 1) ) % 7 ,
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))
)
AS [LastsSundayOfTheMonth]
--Monday [DayOfWeek] = 2.. Need to convert 2 to 0 <-> N + (7 - 2) % 7
DATEADD(DD,
- ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 2) ) % 7 ,
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))
)
AS [LastMondayOfTheMonth]
--Tuesday [DayOfWeek] = 3.. Need to convert 3 to 0 <-> N + (7 - 3) % 7
DATEADD(DD,
- ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 -3) ) % 7 ,
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))
)
AS [LastTuesdayOfTheMonth]
--Wednesday [DayOfWeek] = 4.. Need to convert 4 to 0 <-> N + (7 - 4) % 7
DATEADD(DD,
- ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 4) ) % 7 ,
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))
)
AS [LastWednesdayOfTheMonth]
--Thursday [DayOfWeek] = 5.. Need to convert 5 to 0 <-> N + (7 - 5 ) % 7
DATEADD(DD,
- ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7 - 5) ) % 7 ,
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))
)
AS [LastThursdayOfTheMonth]
--Friday [DayOfWeek] = 6.. Need to convert 6 to 0 <-> N + (7 - 6 ) % 7
DATEADD(DD,
- ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7-6) ) % 7 ,
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))
)
AS [LastFridayOfTheMonth]
--Saturday [DayOfWeek] = 7.. Need to convert 7 to 0 <-> N + (7 - 7 ) % 7
DATEADD(DD,
- ((DATEPART(dw, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))) + (7-7) ) % 7 ,
CONVERT(DATE, DATEADD(DD, - (DATEPART(DD, (DATEADD(MM, 1, DayInTime)))), DATEADD(MM, 1, DayInTime)))
)
AS [LastsSaturdayOfTheMonth]
我希望这对其他人有用,或者有人能够指出执行这些操作的更简单方法。
扩展 [Matts] SQL Server 2012 的答案:
下面的代码适用于 SQL Server 2012 及更高版本,并希望清楚地说明如何识别每一天。 (如果不是,请告诉我,我会澄清)
DECLARE @YearsToPopulate INT = 130;
-- Use the Magic of SQL to identify 1 Jan and then 31st December at the various edges of the required date time frames.
DECLARE @Date1 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,-@YearsToPopulate,GETDATE())), 0);
DECLARE @Date2 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,@YearsToPopulate,GETDATE())) + 1, -1);
DECLARE @RecordsToCreate INT = DATEDIFF(dd,@Date1,@Date2);
WITH MyFullDateRange AS
(
SELECT TOP (@RecordsToCreate) CAST(DATEADD(dd, Number, @Date1) AS DATE) AS DayInTime
FROM ref.[dimNumbers]
)
SELECT DayInTime
--Sunday [DayOfWeek] = 1.. Need to convert 1 to 0 <-> N + (0 - 1) % 7
,LastSundayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 1 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime))
--Monday [DayOfWeek] = 2.. Need to convert 2 to 0 <-> N + (0 - 2) % 7
,LastMondayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 2 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime))
--Tuesday [DayOfWeek] = 3.. Need to convert 2 to 0 <-> N + (0 - 3) % 7
,LastTuesdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 3 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime))
--Wednesday [DayOfWeek] = 4.. Need to convert 4 to 0 <-> N + (0 - 4) % 7
,LastWednesdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 4 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime))
--Thursday [DayOfWeek] = 5.. Need to convert 2 to 0 <-> N + (0 - 5) % 7
,LastThursdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 5 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime))
--Friday [DayOfWeek] = 6.. Need to convert 2 to 0 <-> N + (0 - 6) % 7
,LastFridayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 6 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime))
--Saturday [DayOfWeek] = 7.. Need to convert 2 to 0 <-> N + (0 - 7) % 7
,LastSaturdayOfMonth = DATEADD(DAY, 0 - (@@DATEFIRST - 7 + DATEPART(dw,EOMONTH(DayInTime))) % 7, EOMONTH(DayInTime))
FROM MyFullDateRange;
如果我不必支持 SQL Server 2008,我会使用此代码。
更多 SQL Server 2012 及以上代码:
不幸的是,我所希望的一个好的替代方案并没有起作用。请记住,该解决方案需要适合 dimDate 表。有什么建议可以改进这一点吗?我不喜欢这个解决方案的部分是我需要对 dimDate 表执行两次操作来更新它。
DECLARE @YearsToPopulate INT = 130;
DECLARE @Date1 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,-@YearsToPopulate,GETDATE())), 0);
DECLARE @Date2 DATE = DATEADD(yy, DATEDIFF(yy,0,DATEADD(yyyy,@YearsToPopulate,GETDATE())) + 1, -1);
DECLARE @RecordsToCreate INT = DATEDIFF(dd,@Date1,@Date2);
WITH MyFullDateRange AS
(
SELECT TOP (@RecordsToCreate) CAST(DATEADD(dd, Number, @Date1) AS DATE) AS DayInTime
FROM ref.[dimNumbers]
)
, CreateListOfDatesAndDOWs As
(
Select DayInTime AS DayInTime, DatePART( DW , DayInTime ) AS DayNumber
From MyFullDateRange
)
Select DayInTime AS [currentDate]--SQL 2012 -- DateFromParts(Year(DayInTime),Month(DayInTime) , 1) AS [currentDate]
,LastSun = MAX(CASE WHEN DayNumber=1 THEN DayInTime END)
,LastMon = MAX(CASE WHEN DayNumber=2 THEN DayInTime END)
,LastTue = MAX(CASE WHEN DayNumber=3 THEN DayInTime END)
,LastWed = MAX(CASE WHEN DayNumber=4 THEN DayInTime END)
,LastThu = MAX(CASE WHEN DayNumber=5 THEN DayInTime END)
,LastFri = MAX(CASE WHEN DayNumber=6 THEN DayInTime END)
,LastSat = MAX(CASE WHEN DayNumber=7 THEN DayInTime END)
From CreateListOfDatesAndDOWs
Group By DayInTime --DateFromParts(Year(DayInTime),Month(DayInTime),1)--SQL 2012
因为这会返回以下结果集,对于 dimDate 表来说,这不是我们想要的结果。 (我需要弄清楚如何格式化表格!)
currentDate LastSun LastMon LastTue LastWed LastThu LastFri LastSat
----------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1886-01-01 NULL NULL NULL NULL NULL 1886-01-01 NULL
1886-01-02 NULL NULL NULL NULL NULL NULL 1886-01-02
1886-01-03 1886-01-03 NULL NULL NULL NULL NULL NULL
1886-01-04 NULL 1886-01-04 NULL NULL NULL NULL NULL
1886-01-05 NULL NULL 1886-01-05 NULL NULL NULL NULL
1886-01-06 NULL NULL NULL 1886-01-06 NULL NULL NULL
1886-01-07 NULL NULL NULL NULL 1886-01-07 NULL NULL
1886-01-08 NULL NULL NULL NULL NULL 1886-01-08 NULL
1886-01-09 NULL NULL NULL NULL NULL NULL 1886-01-09
1886-01-10 1886-01-10 NULL NULL NULL NULL NULL NULL
1886-01-11 NULL 1886-01-11 NULL NULL NULL NULL NULL
1886-01-12 NULL NULL 1886-01-12 NULL NULL NULL NULL
1886-01-13 NULL NULL NULL 1886-01-13 NULL NULL NULL
1886-01-14 NULL NULL NULL NULL 1886-01-14 NULL NULL
1886-01-15 NULL NULL NULL NULL NULL 1886-01-15 NULL
1886-01-16 NULL NULL NULL NULL NULL NULL 1886-01-16
1886-01-17 1886-01-17 NULL NULL NULL NULL NULL NULL
1886-01-18 NULL 1886-01-18 NULL NULL NULL NULL NULL
1886-01-19 NULL NULL 1886-01-19 NULL NULL NULL NULL
1886-01-20 NULL NULL NULL 1886-01-20 NULL NULL NULL
1886-01-21 NULL NULL NULL NULL 1886-01-21 NULL NULL
1886-01-22 NULL NULL NULL NULL NULL 1886-01-22 NULL
1886-01-23 NULL NULL NULL NULL NULL NULL 1886-01-23
1886-01-24 1886-01-24 NULL NULL NULL NULL NULL NULL
1886-01-25 NULL 1886-01-25 NULL NULL NULL NULL NULL
1886-01-26 NULL NULL 1886-01-26 NULL NULL NULL NULL
1886-01-27 NULL NULL NULL 1886-01-27 NULL NULL NULL
1886-01-28 NULL NULL NULL NULL 1886-01-28 NULL NULL
1886-01-29 NULL NULL NULL NULL NULL 1886-01-29 NULL
1886-01-30 NULL NULL NULL NULL NULL NULL 1886-01-30
1886-01-31 1886-01-31 NULL NULL NULL NULL NULL NULL
1886-02-01 NULL 1886-02-01 NULL NULL NULL NULL NULL
1886-02-02 NULL NULL 1886-02-02 NULL NULL NULL NULL
1886-02-03 NULL NULL NULL 1886-02-03 NULL NULL NULL
1886-02-04 NULL NULL NULL NULL 1886-02-04 NULL NULL
1886-02-05 NULL NULL NULL NULL NULL 1886-02-05 NULL
1886-02-06 NULL NULL NULL NULL NULL NULL 1886-02-06
最佳答案
这是另一个快速选项。这将返回每个月的最后一个工作日
使用临时计数表和窗口函数 Row_Number()。
只是为了好玩,您可以在第一个、第二个......每月、每季度、每年的星期一进行另一个字段计数
Edit - Added the final conditional aggregation
Declare @Date1 date = '2000-01-01'
Declare @Date2 date = DateAdd(YY,130,@Date1)
;with cte0(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N))
,cteD(D) As (Select Top (DateDiff(DD,@Date1,@Date2)) cast(DateAdd(DD,Row_Number() over (Order By (Select NULL))-1,@Date1) as date) From cte0 N1, cte0 N2, cte0 N3, cte0 N4, cte0 N5, cte0 N6) -- 1 Million
,cte As (
Select Date=D
,DOW = DateName(DW,D)
,DOWPosNeg = Row_Number() over (Partition By Year(D),Month(D),DateName(DW,D) Order by D Desc)
From cteD
)
Select Date = DateFromParts(Year(Date),Month(Date),1)
,LastSun = max(case when DOW='Sunday' then Date else null end)
,LastMon = max(case when DOW='Monday' then Date else null end)
,LastTue = max(case when DOW='Tuesday' then Date else null end)
,LastWed = max(case when DOW='Wednesday' then Date else null end)
,LastThu = max(case when DOW='Thursday' then Date else null end)
,LastFri = max(case when DOW='Friday' then Date else null end)
,LastSat = max(case when DOW='Saturday' then Date else null end)
From cte
Where DOWPosNeg = 1
Group By DateFromParts(Year(Date),Month(Date),1)
Order BY 1
返回
关于SQL Server 日期表 : programatically find the last Sunday in a given month,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40953682/