问题
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