Find the next occurance of a day of the week in SQL

六眼飞鱼酱① 提交于 2019-12-12 02:56:56

问题


I'm trying to update a SQL report sproc's WHERE clause to check whether a given date falls on or before the next occurrence of a class. Classes have a StartDate and occur once per week on the same day each week. Given the StartDate, how can I find the next occurrence of that day of the week?

E.G. If the StartDate is 1/18/2012, a Wednesday, and I run the report as of today, 1/26/2012, I need to find 2/1/2012 which is the next Wednesday after 1/26. If the StartDate is 1/19, a Thurs, and I run the report today, the formula should give me Thurs 1/26 which is today.

Here's sort of the idea in SQL:

SELECT *
FROM tbl_Class cs
INNER JOIN tbl_Enrollment sce ON cs.pk_ClassID = sce.fk_ClassID
WHERE ...
AND sce.StartDate < [Find date of next class after @AsOfDate using cs.StartDate]

回答1:


Here's some example SQL that I came up with. 3 iterations so you can follow how I got to the end. The 3rd iteration should be something you can incorporate into a WHERE clause by substituting your column names for the variables.

Setup:

DECLARE @Startdate DATETIME,@currentdate datetime
SET @Startdate = '1-26-2012' 
SET @Currentdate = '1-23-2012'

--This section just normalizes it so you can use 7 as the interval
--The offset depends on your current setting for DATEFIRST, U.S. English default is 7, Sunday.
-- see http://msdn.microsoft.com/en-us/library/ms187766.aspx 
DECLARE @StartDateWorkingDayOfWeek int,@CurrentDateWorkingDayOfWeek int
SELECT @StartDateWorkingDayOfWeek =(DATEPART(weekday,@Startdate)-2)
SELECT @CurrentDateWorkingDayOfWeek=(DATEPART(weekday,@Currentdate)-2)

Iteration #1

--Iteration 1 
IF @StartDateWorkingDayOfWeek < @CurrentDateWorkingDayOfWeek
SELECT DATEADD(DAY,DATEDIFF(DAY,0,@Currentdate)/7*7 + 7,@StartDateWorkingDayOfWeek)
else
SELECT DATEADD(DAY,DATEDIFF(DAY,0,@Currentdate)/7*7 + 0,@StartDateWorkingDayOfWeek)

Iteration #2

--Iteration 2
SELECT DATEADD(DAY,DATEDIFF(DAY,0,@Currentdate)/7*7 + 

CASE WHEN @StartDateWorkingDayOfWeek < @CurrentDateWorkingDayOfWeek
    then 7
    ELSE 0
    end

    ,@StartDateWorkingDayOfWeek)

Iteration #3

--iteration 3
SELECT DATEADD(DAY,DATEDIFF(DAY,0,@Currentdate)/7*7 + 

CASE WHEN (DATEPART(weekday,@Startdate)-2) < (DATEPART(weekday,@Currentdate)-2)
    then 7
    ELSE 0
    end

    ,(DATEPART(weekday,@Startdate)-2))

Hat tip to this article: http://www.sqlmag.com/article/tsql3/datetime-calculations-part-3




回答2:


Here's what I came up with thanks to TetonSig and his reference to this link: http://www.sqlmag.com/article/tsql3/datetime-calculations-part-3

We can get the date of the previous Monday exclusive of the current date (@AsOfDate) like so:

SELECT DATEADD(day, DATEDIFF(day,0, @AsOfDate-1) /7*7, 0);

This gets the number of days between 1/1/1900 and @AsOfDate in days. /7*7 converts that to whole weeks, and then adds it back to 1/1/1900 (a Mon) to get the Monday before @AsOfDate. The -1 makes it exclusive of @AsOfDate. Without the minus 1, if @AsOfDate were on a Monday, it would be counted as the "previous" Monday.

Next the author shows that to get the inclusive next Monday, we simply need to add 7 to the exclusive previous Monday formula:

SELECT DATEADD(d, DATEDIFF(day,0, @AsOfDate-1) /7*7, 0)+7;

Voila! We've now got the first Monday on or after @AsOfDate. The only problem is, the Monday (0) above is a moving target in my case. I need the first [DayOfWeek] determined by the class date, not the first Monday. I need to swap out a ClassDayOfWeek calculation for the 0s above:

DATEADD(d, DATEDIFF(d, [ClassDayOfWeek], @AsOfDate-1)/7*7, [ClassDayOfWeek])+7

I wanted to calculate the ClassDayOfWeek without being dependent on or having to mess with setting @@datefirst. So I calculated it relative to the base date:

DATEDIFF(d, 0, StartDate)%7

This gives 0 for Mon, 6 for Sun so we can now plug that in for [ClassDayOfWeek]. I should point out that this 0-6 value is dates 1/1/1900-1/7/1900 represented as an int.

DATEADD(d, DATEDIFF(d, DATEDIFF(d, 0, StartDate)%7, @AsOfDate-1)/7*7, DATEDIFF(d, 0, StartDate)%7)+7

And in use per the question:

SELECT *
FROM tbl_Class cs
INNER JOIN tbl_Enrollment sce ON cs.pk_ClassID = sce.fk_ClassID
WHERE ...
AND sce.StartDate < DATEADD(d, 
                            DATEDIFF(d, 
                                     DATEDIFF(d, 0, cs.StartDate)%7, 
                                     @AsOfDate-1)/7*7, 
                            DATEDIFF(d, 0, cs.StartDate)%7)+7



回答3:


I derived the answer with a simple case statement.
In your situation @targetDOW would be the day of the week of the class.

DECLARE @todayDOW INT = DATEPART(dw, GETDATE());
DECLARE @diff INT = (@targetDOW - @todayDOW);

SELECT
    CASE
       WHEN @diff = 0 THEN GETDATE()
       WHEN @diff > 0 THEN DATEADD(d,@diff,GETDATE())
       WHEN @diff < 0 THEN DATEADD(d,@diff + 7,GETDATE())   
    END;


来源:https://stackoverflow.com/questions/9012002/find-the-next-occurance-of-a-day-of-the-week-in-sql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!