我一直在试验以下代码:
SELECT * FROM dbo.NthWeekday(GETDATE(), 1, 1);
SELECT * FROM dbo.NthWeekday(GETDATE(), 1, -1);
无论@@DATEFIRST 设置如何,其中 1 是星期日,7 是星期六。 n 的正值(或 0)将返回下一个第 N 个工作日,而 n 的负值返回前第 N 个工作日。
我不完全理解您想要什么,但如果我收集正确:仅获得第 N 个工作日是不够的。您还想在 X 个月内重复执行此操作。这是我要使用的暂定代码:
DECLARE @date DATE = GETDATE();
DECLARE @numMonths INT = -5
DECLARE @weekday INT = 1;
DECLARE @n INT = 2;
SELECT C.D
FROM dbo.RangeSmallInt(0, @numMonths - SIGN(@numMonths)) A
CROSS APPLY ( -- MonthBegin
SELECT DT = DATEADD(m, DATEDIFF(m, 0, @date) + A.N, 0)
) B
CROSS APPLY dbo.NthWeekday(B.DT, @weekday, @n) C;
Results: 2014-12-14
2015-01-11
2015-02-08
2015-03-08
2015-04-12
您可以将其包装在一个表值函数中,就像我对 NthWeekday 和 RangeSmallInt 所做的那样。 RangeSmallInt 函数调用可以替换为数字表、计数 CTE 或任何您喜欢的术语/样式。
工作原理:
我们首先生成一组以0 开头的数字,因为我们希望函数具有包容性。 (@numMonths - SIGN(@numMonths)) 根据 @numMonths 的符号处理 @numMonths 的 1/0 的加法或减法。这确保为我们的下一个技巧生成正确的整数范围(在上述情况下:0 到 -4)。
一旦我们有一系列整数可以使用,我们就可以使用它们来抵消日期。在这种情况下,我们想要找出 X 个月的月初。如果我们有一个可以返回 Nth Month Begin Date 的函数,那么我们只需将我们已经拥有的整数传递给函数并获取我们想要的日期。这正是我们使用 CROSS APPLY 所做的。
现在我们已经解决了 X 个月的月初问题,我们需要做的就是将 NthWeekday 函数应用于这些日期。
第 N 个工作日:
CREATE FUNCTION dbo.NthWeekday (
@date DATE = NULL
, @weekday INT = NULL
, @n INT = 1
)
RETURNS TABLE
AS
RETURN (
SELECT D = CASE SIGN(@n)
WHEN -1 THEN DATEADD(d, -(DATEPART(dw, @date) + @@DATEFIRST - @weekday) % 7 + ((@n + 1) * 7), @date)
ELSE DATEADD(d, (@weekday - DATEPART(dw, @date) + @@DATEFIRST) % 7 + ((@n - SIGN(@n)) * 7), @date)
END
);
RangeSmallInt:
-- Generate a range of up to 65,536 contiguous BIGINTS
CREATE FUNCTION dbo.RangeSmallInt (
@num1 BIGINT = NULL
, @num2 BIGINT = NULL
)
RETURNS TABLE
AS
RETURN (
WITH Numbers(N) AS (
SELECT N FROM(VALUES
(1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256
) V (N)
)
SELECT TOP (
CASE
WHEN @num1 IS NOT NULL AND @num2 IS NOT NULL THEN ABS(@num1 - @num2) + 1
ELSE 0
END
)
N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + CASE WHEN @num1 <= @num2 THEN @num1 ELSE @num2 END - 1
FROM Numbers A
, Numbers B
WHERE ABS(@num1 - @num2) + 1 < 65537
);