Вот еще одно решение на основе функций с использованием математики даты, которое возвращает следующий N-й день недели в или после заданной даты.Он не использует циклов, чтобы говорить о нем, но может повторяться не более одной итерации, если следующий N-й день недели наступает в следующем месяце.
Эта функция учитывает параметр DATEFIRST для сред, в которых используетсязначение, отличное от значения по умолчанию.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: David Grimberg
-- Create date: 2015-06-18
-- Description: Gets the next Nth weekday
-- @param Date is any date in a month
-- @param DayOfWeek is the weekday of interest ranging
-- from 1 to 7 with @@DATEFIRST being the
-- first day of the week
-- @param NthWeekday represents which ordinal weekday to return.
-- Positive values return dates relative to the start
-- of the month. Negative values return dates relative
-- to the end of the month. Values > 4 indicate the
-- last week, values < -4 indicate the first week.
-- Zero is assumed to be 1.
-- =============================================
ALTER FUNCTION dbo.xxGetNextNthWeekday
(
@Date date,
@NthWeekday smallint,
@DayOfWeek tinyint
)
RETURNS date
AS
BEGIN
DECLARE @FirstOfMonth date
DECLARE @inc int
DECLARE @Result date
-- Clamp the @NthWeekday input to valid values
set @NthWeekday = case when @NthWeekday = 0 then 1
when @NthWeekday > 4 then -1
when @NthWeekday < -4 then 1
else @NthWeekday
end
-- Normalize the requested day of week taking
-- @@DATEFIRST into consideration.
set @DayOfWeek = (@@DATEFIRST + 6 + @DayOfWeek) % 7 + 1
-- Gets the first of the current month or the
-- next month if @NthWeekday is negative.
set @FirstOfMonth = dateadd(month, datediff(month,0,@Date)
+ case when @NthWeekday < 0 then 1 else 0 end
, 0)
-- Add and/or subtract 1 week depending direction of search and the
-- relationship of @FirstOfMonth's Day of the Week to the @DayOfWeek
-- of interest
set @inc = case when (datepart(WEEKDAY, @FirstOfMonth)+@@DATEFIRST-1)%7+1 > @DayOfWeek
then 0
else -1
end
+ case when @NthWeekday < 0 then 1 else 0 end
-- Put it all together
set @Result = dateadd( day
, @DayOfWeek-1
, dateadd( WEEK
, @NthWeekday + @inc
, dateadd( WEEK -- Gets 1st Sunday on or
, datediff(WEEK, -1, @FirstOfMonth)
,-1 ) ) ) -- before @FirstOfMonth
-- [Snip here] --
if @Result < @Date
set @Result = dbo.xxGetNextNthWeekday( dateadd(month, datediff(month, 0, @Date)+1, 0)
, @NthWeekday, @DayOfWeek)
-- [to here for no recursion] --
Return @Result
END
Если вы хотите, чтобы прошедший или будущий N-й день недели текущего месяца основывался на параметре @Date, а не на следующий N-й день недели, вырежьте рекурсивную часть, как указано выше.