我必须每N个月重复一次约会。
我有XDate开始。
我想要第n周的第m个工作日日期。
N代表2 - I have to get for every 2 month
XDate假设明天的日期。所以,Starting from tomorrow
m是7 - 所以,get date of every Saturday
n是2 - of second week.
我甚至无法想到这个复杂逻辑的起点。
任何建议我应该如何开始 - 伪代码
提前致谢,
答案 0 :(得分:1)
首先,这是一个日历表派上用场的地方。下面的代码创建一个名为calendar的表,并使用从2000开始的日期填充它。它还有一个名为NthWeekdayInMonth的列。例如,如果您查看1/29/05到1/31/05的条目,您会看到此列设置为5,因为这些是本月的第5个星期六,星期日和星期一。
CREATE TABLE Calendar
(
[Date] date NOT NULL,
[NthWeekdayInMonth] int,
CONSTRAINT PK_Calendar
PRIMARY KEY CLUSTERED ([Date])
WITH FILLFACTOR = 100
)
;WITH cte AS
(
SELECT
DATEADD(d, (a.Number * 256) + b.Number, '01/01/2000') AS [Date]
FROM
(
SELECT number
FROM master..spt_values
WHERE
type = 'P'
AND number <= 255
) a (Number),
(
SELECT number
FROM master..spt_values
WHERE
type = 'P'
AND number <= 255
) b (Number)
)
INSERT INTO Calendar
SELECT
[Date],
ROW_NUMBER() OVER (PARTITION BY YEAR([Date]), MONTH([Date]), DATEPART(dw, [Date]) ORDER BY [Date]) FROM cte
ORDER BY
[Date]
GO
现在我们有一个日历表,剩下的就相当简单了。我确实在一个方面偏离了你的设计,但你应该能够根据需要进行调整。在我的实现中,开始日期实际上是应该返回的第一个日期。因此,2014年1月11日的开始日期,每2个月会回来一次:
2014-01-11
2014-03-08
2014-05-10
2014-07-12
通过传递第一个日期,代码可以确定一周中的哪一天以及该月的哪一周。传递这些值是多余的。测试代码如下......
DECLARE @startDate date
DECLARE @everyNMonths int
DECLARE @numResults int
DECLARE @nthAppearanceOfDay int
SET @startDate = '01/11/2014' -- First occurence is on this date
SET @everyNMonths = 2 -- Skip every n months
SET @numResults = 4 -- Max # of results to return
-- Figure out which x-day of the month this is. For example, if the starting
-- date is 1/11/2014 that was the second Saturday so this will be set to 2.
SELECT @nthAppearanceOfDay = NthWeekdayInMonth FROM calendar WHERE [date] = @startDate
-- Use a CTE to get all the months involved in this calculation
;WITH candidateMonths AS (
SELECT
1 AS [resultnum], @startDate AS [date]
UNION ALL
SELECT resultnum + 1, DATEADD(month, @everyNMonths, [date]) FROM candidateMonths
WHERE resultnum + 1 <= @numResults
)
-- Now evaluate every date for each of the candidate months. If the day of week matches
-- that of the start date AND it is the Nth occurrence of that day of week in the month
-- include it
SELECT
c.[Date]
FROM
candidateMonths cm
INNER JOIN calendar c ON ( (YEAR(c.[Date]) = YEAR(cm.[Date])) AND (MONTH(c.[Date]) = MONTH(cm.[Date])))
WHERE
(DATEPART(dw, c.[date]) = DATEPART(dw, @startDate)) -- Same day of week
AND
(c.NthWeekdayInMonth = @nthAppearanceOfDay) -- Same week of month
答案 1 :(得分:0)
我一直在尝试以下代码:
SELECT * FROM dbo.NthWeekday(GETDATE(), 1, 1);
SELECT * FROM dbo.NthWeekday(GETDATE(), 1, -1);
无论@@ DATEFIRST设置如何,其中1是星期日,7是星期六。 n(或0)的正值将返回 Next Nth Weekday ,而n的负值将返回 Previous Nth Weekday 。
我不完全明白你想要什么但是如果我正确地聚集在一起:仅仅获得第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个月的月初。如果我们有一个可以返回第N个月开始日期的函数,那么我们只需将已有的整数传递给函数并输出我们想要的日期。这正是我们使用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
);