T-SQL 计算日期的重复次数
T-SQL Calculate recurrences of a Date
我正在研究计算一周重复的 CTE,但是当 模式 跨年时我遇到了一些问题。
CTE 应该
根据以下参数计算所有出现次数:
- 重复次数 - 会发生多少次
- 工作日 - 它将在一周中的哪一天发生
- 开始日期 - 模式计算开始的时间
- 周期性 - 以周为单位的频率,即每周 1 次,每 2 周 2 次
- 开始周 - 第一次出现的周数,反映了开始日期列
这是我存储模式的方式:
/*
Pattern Table
*/
CREATE TABLE Pattern (
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY
, [Subject] [nvarchar](100) NULL
, [RecurrenceCount] [int] NULL
, [WeekDays] [varchar](max) NULL
, [StartDate] [datetime] NULL
, [EndDate] [datetime] NULL
, [Periodicity] [int] NULL
, [StartingWeek] [int] NULL
);
下面是我用来测试 CTE 的几个模式:
/*
Pattern samples for test
*/
Insert into Pattern Values (N'Every 5 Weeks Fri, Sat, Sun', 72, 'Friday, Saturday, Sunday', N'2016-12-02', N'2016-12-02', 5, datepart(wk, N'2016-12-02'));
Insert into Pattern Values (N'Every 3 Weeks Tue, Wed, Thu', 20, 'Tuesday, Wednesday, Thursday', N'2016-11-01', N'2016-11-01', 3, datepart(wk, N'2016-11-01'));
考虑到一周的第一天,我开始计算 星期一
SET DATEFIRST 1
这是我用来 运行 这个计算的 CTE:
/*
Display Patterns
*/
select * from Pattern
DECLARE @mindate DATE = (SELECT MIN(StartDate) FROM Pattern)
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM Pattern)
DECLARE @maxcount INT = (SELECT MAX(RecurrenceCount) FROM Pattern)
DECLARE @maxdate DATE = DATEADD(WK, @maxcount + 10, @maxmindate)
/*
CTE to generate required occurrences
*/
;With cteKeyDate As (
Select
KeyStartDate = @MinDate,
KeyDOW = DateName(WEEKDAY, @MinDate),
KeyWeek = datepart(WK,@MinDate)
Union All
Select
KeyStartDate = DateAdd(DD, 1, df.KeyStartDate) ,
KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate)),
KeyWeek= DatePart(WK,DateAdd(DD, 1, df.KeyStartDate))
From cteKeyDate DF
Where DF.KeyStartDate <= @MaxDate
)
SELECT
Id, KeyStartDate, KeyDow, KeyWeek, RowNr, OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate)
FROM
(Select
A.Id
,A.StartDate
,A.EndDate
,Count = A.RecurrenceCount
,Days = A.WeekDays
,Every = A.Periodicity
,KeyStartDate = CASE
/*
if no periodicity (1) then it is sequential
if periodicity, first week doesn't apply (MIN KeyWeek)
*/
WHEN A.Periodicity = 1
OR ( Periodicity <> 1 AND (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) = KeyWeek )
THEN KeyStartDate
/* Otherwise formula ADD WEEKS => Current Week Min Week */
ELSE
DATEADD( WK, ((A.Periodicity - 1) * ( KeyWeek - (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) ) ) , KeyStartDate )
END
,KeyDow
,KeyWeek
,RowNr = Row_Number() over (Partition By A.Id Order By B.KeyStartDate)
,Periodicity = A.Periodicity
from
Pattern A
Join cteKeyDate B on B.KeyStartDate >= DATEADD(DAY, -1, A.StartDate) and Charindex(KeyDOW, A.WeekDays) > 0
) Final
Where
RowNr <= Count AND Id = 1
Option (maxrecursion 32767)
现在,如果我再次测试我的模式,例如,第一个,我得到这个结果,当明年发生时,它有错误。 RowNr 15 是错误的,因为它应该发生在 4 月 23 日(星期日)而不是下周。
Id KeyStartDate KeyDow KeyWeek RowNr OccNr
1 02.12.2016 Friday 49 1 1
2 03.12.2016 Saturday 49 2 2
3 04.12.2016 Sunday 49 3 3
4 06.01.2017 Friday 50 4 4
5 07.01.2017 Saturday 50 5 5
6 08.01.2017 Sunday 50 6 6
7 10.02.2017 Friday 51 7 7
8 11.02.2017 Saturday 51 8 8
9 12.02.2017 Sunday 51 9 9
10 17.03.2017 Friday 52 10 10
11 18.03.2017 Saturday 52 11 11
12 19.03.2017 Sunday 52 12 12
13 21.04.2017 Friday 53 13 13
14 22.04.2017 Saturday 53 14 14
15 28.04.2013 Sunday 1 15 15
16 31.05.2013 Friday 2 16 16
17 01.06.2013 Saturday 2 17 17
而第二个模式计算得很好。我认为当模式跨越一年并且周数在 SQL 中重置为 0 时我的逻辑有问题但是我找不到解决方案,我现在挣扎了几天。
您可以使用示例执行代码 here。
您遇到该问题的原因是您正在使用日期部分获取周数,因此一旦年份更改,日期部分将返回到 1。这应该更改...
我做了这个更改,日期现在按顺序排列,检查一下。
顺便说一句,更改是第一个 CTE。
/*
Display Patterns
*/
select * from Pattern
DECLARE @mindate DATE = (SELECT MIN(StartDate) FROM Pattern)
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM Pattern)
DECLARE @maxcount INT = (SELECT MAX(RecurrenceCount) FROM Pattern)
DECLARE @maxdate DATE = DATEADD(WK, @maxcount + 10, @maxmindate)
declare @minWeekPart INT = DATEPART(WK,@MinDate)
/*
CTE to generate required occurrences
*/
;With cteKeyDate As (
Select
KeyStartDate = @MinDate,
KeyDOW = DateName(WEEKDAY, @MinDate),
KeyWeek = @minWeekPart
Union All
Select
KeyStartDate = DateAdd(DD, 1, df.KeyStartDate) ,
KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate)),
KeyWeek= @minWeekPart + datediff(WK,@MinDate,DateAdd(DD, 1, df.KeyStartDate))
From cteKeyDate DF
Where DF.KeyStartDate <= @MaxDate
)
--select * from cteKeyDate
-- order by 1
--Option (maxrecursion 32767)
SELECT
Id, KeyStartDate, KeyDow, KeyWeek, RowNr, OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate)
FROM
(Select
A.Id
,A.StartDate
,A.EndDate
,Count = A.RecurrenceCount
,Days = A.WeekDays
,Every = A.Periodicity
,KeyStartDate = CASE
/*
if no periodicity (1) then it is sequential
if periodicity, first week doesn't apply (MIN KeyWeek)
*/
WHEN A.Periodicity = 1
OR ( Periodicity <> 1 AND (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) = KeyWeek )
THEN KeyStartDate
/* Otherwise formula ADD WEEKS => Current Week Min Week */
ELSE
DATEADD( WK, ((A.Periodicity - 1) * ( KeyWeek - (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) ) ) , KeyStartDate )
END
,KeyDow
,KeyWeek
,RowNr = Row_Number() over (Partition By A.Id Order By B.KeyStartDate)
,Periodicity = A.Periodicity
from
Pattern A
Join cteKeyDate B on B.KeyStartDate >= DATEADD(DAY, -1, A.StartDate) and Charindex(KeyDOW, A.WeekDays) > 0
) Final
Where
RowNr <= Count AND Id = 1
order by 2
Option (maxrecursion 32767)
我在 rextester 中做了 运行,你可以在这里找到它 --> http://rextester.com/GWEY37271
花点时间在这上面。您使用的计算有缺陷。除非您的问题中没有指出特殊规则,否则我不明白为什么有些日期很特别。我更喜欢使用变量 tables.
/*
Pattern Table
*/
DECLARE @Pattern TABLE(
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY
,[Subject] [nvarchar](100) NULL
,[RecurrenceCount] [int] NULL
,[WeekDays] [varchar](max) NULL
,[StartDate] [datetime] NULL
,[EndDate] [datetime] NULL
,[Periodicity] [int] NULL
,[StartingWeek] [int] NULL
);
/*
Populate with values based on Recurreance and Startdate. The startdate will give the start week, which make the start week obsolete.
*/
DECLARE @PreferredDate TABLE(
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY
,[PreferredDate] [datetime] NULL
,[PreferredWeek] [int] NULL
,[PreferredYear] [int] NULL
)
始终检索 datefirst 的当前设置非常重要。如果他们使用其他设置,您将破坏其他人的计算。出于显而易见的原因,我还添加了模式 ID。
DECLARE @DateFirst int = @@dateFirst --DATEFIRST is a global setting
DECLARE @mindate DATE = (SELECT MIN(StartDate) FROM @Pattern WHERE id=@PreferredSubjectID)
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM @Pattern WHERE Id=@PreferredSubjectID)
DECLARE @maxcount INT = (SELECT MAX(RecurrenceCount) FROM @Pattern WHERE Id=@PreferredSubjectID)
DECLARE @maxdate DATE = DATEADD(WK, @maxcount + 50, @maxmindate)
SET DATEFIRST 1
DECLARE @PreferredSubjectID int = 1
@preferreddate table 使用以下内容填充:
/*
CTE to generate required preferred dates
*/
;With ctePreferredDate AS (
Select PreferredDate = @MinDate, PreferredWeek = DATEPART(WK, @MinDate), PreferredYear = DATEPART(YYYY, @MinDate)
Union All
SELECT PreferredDate = DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE Id=@PreferredSubjectID), PreferredDate)
,PreferredWeek = DATEPART(WK,DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE Id=@PreferredSubjectID), PreferredDate))
,PreferredYear = DATEPART(yyyy,DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE Id=@PreferredSubjectID), PreferredDate))
From ctePreferredDate pFD
Where pFD.PreferredDate <= @MaxDate
)
INSERT INTO @PreferredDate (PreferredDate, PreferredWeek, PreferredYear)
SELECT PreferredDate, PreferredWeek, PreferredYear
FROM ctePreferredDate
最终的 CTE table 使用以下内容填充:
/*
CTE to generate required occurrences
*/
;With cteKeyDate As (
Select KeyStartDate = @MinDate
,KeyDOW = DateName(WEEKDAY, @MinDate)
,KeyWeek = datepart(WK,@MinDate)
,id = @PreferredSubjectID
,KeyOccurrance = @maxcount
Union All
Select KeyStartDate = DateAdd(DD, 1, df.KeyStartDate)
,KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate))
,KeyWeek= DatePart(WK,DateAdd(DD, 1, df.KeyStartDate))
,id=@PreferredSubjectID
,KeyOccurrance = @maxcount
From cteKeyDate DF
Where DF.KeyStartDate <= @MaxDate
)
SELECT StartDate
,[DayOfWeek]
,[Week]
,OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate)
FROM
(
SELECT cte.KeyStartDate AS StartDate
,cte.KeyDOW AS [DayOfWeek]
,cte.KeyWeek AS [Week]
,cte.id
,cte.KeyOccurrance AS Occurrance
,RowNr = ROW_NUMBER() OVER (PARTITION BY KeyOccurrance ORDER BY KeyStartDate)
FROM cteKeyDate cte
INNER JOIN
@PreferredDate pfd
ON cte.KeyWeek = pfd.PreferredWeek
AND YEAR(cte.KeyStartDate) = pfd.PreferredYear
WHERE cte.KeyDOW IN (SELECT LTRIM(RTRIM(Item)) FROM fn_SplitString((SELECT weekdays from @Pattern WHERE Id=1),','))
)cte
WHERE cte.RowNr <= cte.Occurrance
ORDER BY cte.StartDate
Option (maxrecursion 32767)
SET DATEFIRST @DateFirst --Quite important
结果
2016/12/02 Friday 49 1
2016/12/03 Saturday 49 2
2016/12/04 Sunday 49 3
2017/01/06 Friday 2 4
2017/01/07 Saturday 2 5
2017/01/08 Sunday 2 6
2017/02/10 Friday 7 7
2017/02/11 Saturday 7 8
2017/02/12 Sunday 7 9
2017/03/17 Friday 12 10
2017/03/18 Saturday 12 11
2017/03/19 Sunday 12 12
2017/04/21 Friday 17 13
2017/04/22 Saturday 17 14
2017/04/23 Sunday 17 15
2017/05/26 Friday 22 16
2017/05/27 Saturday 22 17
2017/05/28 Sunday 22 18
2017/06/30 Friday 27 19
2017/07/01 Saturday 27 20
2017/07/02 Sunday 27 21
2017/08/04 Friday 32 22
2017/08/05 Saturday 32 23
2017/08/06 Sunday 32 24
2017/09/08 Friday 37 25
2017/09/09 Saturday 37 26
2017/09/10 Sunday 37 27
2017/10/13 Friday 42 28
2017/10/14 Saturday 42 29
2017/10/15 Sunday 42 30
2017/11/17 Friday 47 31
2017/11/18 Saturday 47 32
2017/11/19 Sunday 47 33
2017/12/22 Friday 52 34
2017/12/23 Saturday 52 35
2017/12/24 Sunday 52 36
2018/01/26 Friday 4 37
2018/01/27 Saturday 4 38
2018/01/28 Sunday 4 39
2018/03/02 Friday 9 40
2018/03/03 Saturday 9 41
2018/03/04 Sunday 9 42
2018/04/06 Friday 14 43
2018/04/07 Saturday 14 44
2018/04/08 Sunday 14 45
2018/05/11 Friday 19 46
2018/05/12 Saturday 19 47
2018/05/13 Sunday 19 48
2018/06/15 Friday 24 49
2018/06/16 Saturday 24 50
2018/06/17 Sunday 24 51
2018/07/20 Friday 29 52
2018/07/21 Saturday 29 53
2018/07/22 Sunday 29 54
2018/08/24 Friday 34 55
2018/08/25 Saturday 34 56
2018/08/26 Sunday 34 57
2018/09/28 Friday 39 58
2018/09/29 Saturday 39 59
2018/09/30 Sunday 39 60
2018/11/02 Friday 44 61
2018/11/03 Saturday 44 62
2018/11/04 Sunday 44 63
2018/12/07 Friday 49 64
2018/12/08 Saturday 49 65
2018/12/09 Sunday 49 66
2019/01/11 Friday 2 67
2019/01/12 Saturday 2 68
2019/01/13 Sunday 2 69
2019/02/15 Friday 7 70
2019/02/16 Saturday 7 71
2019/02/17 Sunday 7 72
拆分字符串函数:
ALTER FUNCTION [dbo].[fn_SplitString](
@InputStr varchar(Max),
@Seperator varchar(10))
RETURNS @OutStrings TABLE (ItemNo int identity(1,1), Item varchar(256))
AS
BEGIN
DECLARE @Str varchar(2000),
@Poz int, @cnt int
--DECLARE @OutStrings TABLE (Item varchar(2000))
SELECT @Poz = CHARINDEX (@Seperator, @InputStr), @cnt = 0
WHILE @Poz > 0 AND @cnt <= 10000
BEGIN
SELECT @Str = SubString(@InputStr, 1, @Poz - 1)
INSERT INTO @OutStrings(Item) VALUES(@Str)
SELECT @InputStr = Right(@Inputstr, Len(@InputStr) - (len(@Str) + len(@Seperator)))
SELECT @Poz = CHARINDEX (@Seperator, @InputStr), @cnt = @cnt + 1
END
IF @InputStr <> ''
BEGIN
INSERT INTO @OutStrings(Item) VALUES(@InputStr)
END
RETURN
END
我正在研究计算一周重复的 CTE,但是当 模式 跨年时我遇到了一些问题。
CTE 应该 根据以下参数计算所有出现次数:
- 重复次数 - 会发生多少次
- 工作日 - 它将在一周中的哪一天发生
- 开始日期 - 模式计算开始的时间
- 周期性 - 以周为单位的频率,即每周 1 次,每 2 周 2 次
- 开始周 - 第一次出现的周数,反映了开始日期列
这是我存储模式的方式:
/*
Pattern Table
*/
CREATE TABLE Pattern (
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY
, [Subject] [nvarchar](100) NULL
, [RecurrenceCount] [int] NULL
, [WeekDays] [varchar](max) NULL
, [StartDate] [datetime] NULL
, [EndDate] [datetime] NULL
, [Periodicity] [int] NULL
, [StartingWeek] [int] NULL
);
下面是我用来测试 CTE 的几个模式:
/*
Pattern samples for test
*/
Insert into Pattern Values (N'Every 5 Weeks Fri, Sat, Sun', 72, 'Friday, Saturday, Sunday', N'2016-12-02', N'2016-12-02', 5, datepart(wk, N'2016-12-02'));
Insert into Pattern Values (N'Every 3 Weeks Tue, Wed, Thu', 20, 'Tuesday, Wednesday, Thursday', N'2016-11-01', N'2016-11-01', 3, datepart(wk, N'2016-11-01'));
考虑到一周的第一天,我开始计算 星期一
SET DATEFIRST 1
这是我用来 运行 这个计算的 CTE:
/*
Display Patterns
*/
select * from Pattern
DECLARE @mindate DATE = (SELECT MIN(StartDate) FROM Pattern)
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM Pattern)
DECLARE @maxcount INT = (SELECT MAX(RecurrenceCount) FROM Pattern)
DECLARE @maxdate DATE = DATEADD(WK, @maxcount + 10, @maxmindate)
/*
CTE to generate required occurrences
*/
;With cteKeyDate As (
Select
KeyStartDate = @MinDate,
KeyDOW = DateName(WEEKDAY, @MinDate),
KeyWeek = datepart(WK,@MinDate)
Union All
Select
KeyStartDate = DateAdd(DD, 1, df.KeyStartDate) ,
KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate)),
KeyWeek= DatePart(WK,DateAdd(DD, 1, df.KeyStartDate))
From cteKeyDate DF
Where DF.KeyStartDate <= @MaxDate
)
SELECT
Id, KeyStartDate, KeyDow, KeyWeek, RowNr, OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate)
FROM
(Select
A.Id
,A.StartDate
,A.EndDate
,Count = A.RecurrenceCount
,Days = A.WeekDays
,Every = A.Periodicity
,KeyStartDate = CASE
/*
if no periodicity (1) then it is sequential
if periodicity, first week doesn't apply (MIN KeyWeek)
*/
WHEN A.Periodicity = 1
OR ( Periodicity <> 1 AND (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) = KeyWeek )
THEN KeyStartDate
/* Otherwise formula ADD WEEKS => Current Week Min Week */
ELSE
DATEADD( WK, ((A.Periodicity - 1) * ( KeyWeek - (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) ) ) , KeyStartDate )
END
,KeyDow
,KeyWeek
,RowNr = Row_Number() over (Partition By A.Id Order By B.KeyStartDate)
,Periodicity = A.Periodicity
from
Pattern A
Join cteKeyDate B on B.KeyStartDate >= DATEADD(DAY, -1, A.StartDate) and Charindex(KeyDOW, A.WeekDays) > 0
) Final
Where
RowNr <= Count AND Id = 1
Option (maxrecursion 32767)
现在,如果我再次测试我的模式,例如,第一个,我得到这个结果,当明年发生时,它有错误。 RowNr 15 是错误的,因为它应该发生在 4 月 23 日(星期日)而不是下周。
Id KeyStartDate KeyDow KeyWeek RowNr OccNr
1 02.12.2016 Friday 49 1 1
2 03.12.2016 Saturday 49 2 2
3 04.12.2016 Sunday 49 3 3
4 06.01.2017 Friday 50 4 4
5 07.01.2017 Saturday 50 5 5
6 08.01.2017 Sunday 50 6 6
7 10.02.2017 Friday 51 7 7
8 11.02.2017 Saturday 51 8 8
9 12.02.2017 Sunday 51 9 9
10 17.03.2017 Friday 52 10 10
11 18.03.2017 Saturday 52 11 11
12 19.03.2017 Sunday 52 12 12
13 21.04.2017 Friday 53 13 13
14 22.04.2017 Saturday 53 14 14
15 28.04.2013 Sunday 1 15 15
16 31.05.2013 Friday 2 16 16
17 01.06.2013 Saturday 2 17 17
而第二个模式计算得很好。我认为当模式跨越一年并且周数在 SQL 中重置为 0 时我的逻辑有问题但是我找不到解决方案,我现在挣扎了几天。
您可以使用示例执行代码 here。
您遇到该问题的原因是您正在使用日期部分获取周数,因此一旦年份更改,日期部分将返回到 1。这应该更改... 我做了这个更改,日期现在按顺序排列,检查一下。 顺便说一句,更改是第一个 CTE。
/*
Display Patterns
*/
select * from Pattern
DECLARE @mindate DATE = (SELECT MIN(StartDate) FROM Pattern)
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM Pattern)
DECLARE @maxcount INT = (SELECT MAX(RecurrenceCount) FROM Pattern)
DECLARE @maxdate DATE = DATEADD(WK, @maxcount + 10, @maxmindate)
declare @minWeekPart INT = DATEPART(WK,@MinDate)
/*
CTE to generate required occurrences
*/
;With cteKeyDate As (
Select
KeyStartDate = @MinDate,
KeyDOW = DateName(WEEKDAY, @MinDate),
KeyWeek = @minWeekPart
Union All
Select
KeyStartDate = DateAdd(DD, 1, df.KeyStartDate) ,
KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate)),
KeyWeek= @minWeekPart + datediff(WK,@MinDate,DateAdd(DD, 1, df.KeyStartDate))
From cteKeyDate DF
Where DF.KeyStartDate <= @MaxDate
)
--select * from cteKeyDate
-- order by 1
--Option (maxrecursion 32767)
SELECT
Id, KeyStartDate, KeyDow, KeyWeek, RowNr, OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate)
FROM
(Select
A.Id
,A.StartDate
,A.EndDate
,Count = A.RecurrenceCount
,Days = A.WeekDays
,Every = A.Periodicity
,KeyStartDate = CASE
/*
if no periodicity (1) then it is sequential
if periodicity, first week doesn't apply (MIN KeyWeek)
*/
WHEN A.Periodicity = 1
OR ( Periodicity <> 1 AND (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) = KeyWeek )
THEN KeyStartDate
/* Otherwise formula ADD WEEKS => Current Week Min Week */
ELSE
DATEADD( WK, ((A.Periodicity - 1) * ( KeyWeek - (SELECT MIN(C.StartingWeek) FROM Pattern AS C WHERE C.Id = A.Id) ) ) , KeyStartDate )
END
,KeyDow
,KeyWeek
,RowNr = Row_Number() over (Partition By A.Id Order By B.KeyStartDate)
,Periodicity = A.Periodicity
from
Pattern A
Join cteKeyDate B on B.KeyStartDate >= DATEADD(DAY, -1, A.StartDate) and Charindex(KeyDOW, A.WeekDays) > 0
) Final
Where
RowNr <= Count AND Id = 1
order by 2
Option (maxrecursion 32767)
我在 rextester 中做了 运行,你可以在这里找到它 --> http://rextester.com/GWEY37271
花点时间在这上面。您使用的计算有缺陷。除非您的问题中没有指出特殊规则,否则我不明白为什么有些日期很特别。我更喜欢使用变量 tables.
/*
Pattern Table
*/
DECLARE @Pattern TABLE(
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY
,[Subject] [nvarchar](100) NULL
,[RecurrenceCount] [int] NULL
,[WeekDays] [varchar](max) NULL
,[StartDate] [datetime] NULL
,[EndDate] [datetime] NULL
,[Periodicity] [int] NULL
,[StartingWeek] [int] NULL
);
/*
Populate with values based on Recurreance and Startdate. The startdate will give the start week, which make the start week obsolete.
*/
DECLARE @PreferredDate TABLE(
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY
,[PreferredDate] [datetime] NULL
,[PreferredWeek] [int] NULL
,[PreferredYear] [int] NULL
)
始终检索 datefirst 的当前设置非常重要。如果他们使用其他设置,您将破坏其他人的计算。出于显而易见的原因,我还添加了模式 ID。
DECLARE @DateFirst int = @@dateFirst --DATEFIRST is a global setting
DECLARE @mindate DATE = (SELECT MIN(StartDate) FROM @Pattern WHERE id=@PreferredSubjectID)
DECLARE @maxmindate DATE = (SELECT MAX(StartDate) FROM @Pattern WHERE Id=@PreferredSubjectID)
DECLARE @maxcount INT = (SELECT MAX(RecurrenceCount) FROM @Pattern WHERE Id=@PreferredSubjectID)
DECLARE @maxdate DATE = DATEADD(WK, @maxcount + 50, @maxmindate)
SET DATEFIRST 1
DECLARE @PreferredSubjectID int = 1
@preferreddate table 使用以下内容填充:
/*
CTE to generate required preferred dates
*/
;With ctePreferredDate AS (
Select PreferredDate = @MinDate, PreferredWeek = DATEPART(WK, @MinDate), PreferredYear = DATEPART(YYYY, @MinDate)
Union All
SELECT PreferredDate = DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE Id=@PreferredSubjectID), PreferredDate)
,PreferredWeek = DATEPART(WK,DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE Id=@PreferredSubjectID), PreferredDate))
,PreferredYear = DATEPART(yyyy,DATEADD(WK,(SELECT Periodicity FROM @Pattern WHERE Id=@PreferredSubjectID), PreferredDate))
From ctePreferredDate pFD
Where pFD.PreferredDate <= @MaxDate
)
INSERT INTO @PreferredDate (PreferredDate, PreferredWeek, PreferredYear)
SELECT PreferredDate, PreferredWeek, PreferredYear
FROM ctePreferredDate
最终的 CTE table 使用以下内容填充:
/*
CTE to generate required occurrences
*/
;With cteKeyDate As (
Select KeyStartDate = @MinDate
,KeyDOW = DateName(WEEKDAY, @MinDate)
,KeyWeek = datepart(WK,@MinDate)
,id = @PreferredSubjectID
,KeyOccurrance = @maxcount
Union All
Select KeyStartDate = DateAdd(DD, 1, df.KeyStartDate)
,KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyStartDate))
,KeyWeek= DatePart(WK,DateAdd(DD, 1, df.KeyStartDate))
,id=@PreferredSubjectID
,KeyOccurrance = @maxcount
From cteKeyDate DF
Where DF.KeyStartDate <= @MaxDate
)
SELECT StartDate
,[DayOfWeek]
,[Week]
,OccNr = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY StartDate)
FROM
(
SELECT cte.KeyStartDate AS StartDate
,cte.KeyDOW AS [DayOfWeek]
,cte.KeyWeek AS [Week]
,cte.id
,cte.KeyOccurrance AS Occurrance
,RowNr = ROW_NUMBER() OVER (PARTITION BY KeyOccurrance ORDER BY KeyStartDate)
FROM cteKeyDate cte
INNER JOIN
@PreferredDate pfd
ON cte.KeyWeek = pfd.PreferredWeek
AND YEAR(cte.KeyStartDate) = pfd.PreferredYear
WHERE cte.KeyDOW IN (SELECT LTRIM(RTRIM(Item)) FROM fn_SplitString((SELECT weekdays from @Pattern WHERE Id=1),','))
)cte
WHERE cte.RowNr <= cte.Occurrance
ORDER BY cte.StartDate
Option (maxrecursion 32767)
SET DATEFIRST @DateFirst --Quite important
结果
2016/12/02 Friday 49 1
2016/12/03 Saturday 49 2
2016/12/04 Sunday 49 3
2017/01/06 Friday 2 4
2017/01/07 Saturday 2 5
2017/01/08 Sunday 2 6
2017/02/10 Friday 7 7
2017/02/11 Saturday 7 8
2017/02/12 Sunday 7 9
2017/03/17 Friday 12 10
2017/03/18 Saturday 12 11
2017/03/19 Sunday 12 12
2017/04/21 Friday 17 13
2017/04/22 Saturday 17 14
2017/04/23 Sunday 17 15
2017/05/26 Friday 22 16
2017/05/27 Saturday 22 17
2017/05/28 Sunday 22 18
2017/06/30 Friday 27 19
2017/07/01 Saturday 27 20
2017/07/02 Sunday 27 21
2017/08/04 Friday 32 22
2017/08/05 Saturday 32 23
2017/08/06 Sunday 32 24
2017/09/08 Friday 37 25
2017/09/09 Saturday 37 26
2017/09/10 Sunday 37 27
2017/10/13 Friday 42 28
2017/10/14 Saturday 42 29
2017/10/15 Sunday 42 30
2017/11/17 Friday 47 31
2017/11/18 Saturday 47 32
2017/11/19 Sunday 47 33
2017/12/22 Friday 52 34
2017/12/23 Saturday 52 35
2017/12/24 Sunday 52 36
2018/01/26 Friday 4 37
2018/01/27 Saturday 4 38
2018/01/28 Sunday 4 39
2018/03/02 Friday 9 40
2018/03/03 Saturday 9 41
2018/03/04 Sunday 9 42
2018/04/06 Friday 14 43
2018/04/07 Saturday 14 44
2018/04/08 Sunday 14 45
2018/05/11 Friday 19 46
2018/05/12 Saturday 19 47
2018/05/13 Sunday 19 48
2018/06/15 Friday 24 49
2018/06/16 Saturday 24 50
2018/06/17 Sunday 24 51
2018/07/20 Friday 29 52
2018/07/21 Saturday 29 53
2018/07/22 Sunday 29 54
2018/08/24 Friday 34 55
2018/08/25 Saturday 34 56
2018/08/26 Sunday 34 57
2018/09/28 Friday 39 58
2018/09/29 Saturday 39 59
2018/09/30 Sunday 39 60
2018/11/02 Friday 44 61
2018/11/03 Saturday 44 62
2018/11/04 Sunday 44 63
2018/12/07 Friday 49 64
2018/12/08 Saturday 49 65
2018/12/09 Sunday 49 66
2019/01/11 Friday 2 67
2019/01/12 Saturday 2 68
2019/01/13 Sunday 2 69
2019/02/15 Friday 7 70
2019/02/16 Saturday 7 71
2019/02/17 Sunday 7 72
拆分字符串函数:
ALTER FUNCTION [dbo].[fn_SplitString](
@InputStr varchar(Max),
@Seperator varchar(10))
RETURNS @OutStrings TABLE (ItemNo int identity(1,1), Item varchar(256))
AS
BEGIN
DECLARE @Str varchar(2000),
@Poz int, @cnt int
--DECLARE @OutStrings TABLE (Item varchar(2000))
SELECT @Poz = CHARINDEX (@Seperator, @InputStr), @cnt = 0
WHILE @Poz > 0 AND @cnt <= 10000
BEGIN
SELECT @Str = SubString(@InputStr, 1, @Poz - 1)
INSERT INTO @OutStrings(Item) VALUES(@Str)
SELECT @InputStr = Right(@Inputstr, Len(@InputStr) - (len(@Str) + len(@Seperator)))
SELECT @Poz = CHARINDEX (@Seperator, @InputStr), @cnt = @cnt + 1
END
IF @InputStr <> ''
BEGIN
INSERT INTO @OutStrings(Item) VALUES(@InputStr)
END
RETURN
END