【发布时间】:2017-04-13 09:58:29
【问题描述】:
我正在研究计算一周重复次数的 CTE,但是当 模式 跨年时我遇到了一些问题。
CTE 应该 根据以下参数计算所有出现次数:
- 重复次数 - 会发生多少次
- 工作日 - 一周中的哪一天发生
- 开始日期 - 模式计算何时开始
- 周期性 - 以周为单位的频率,即每周 1 次,每 2 周 2 次em>
- 开始周 - 第一次出现的周数,反映开始日期列
这就是我存储模式的方式:
/*
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 执行代码。
【问题讨论】:
-
作为参考,您不需要在您的
ROW_NUMBER中使用PARTITION BY,并且请不要使用A、B部署代码,C表别名。 -
@iamdave 关于别名,我不使用 A、B、C,但 A 代表“活动”,因此需要一个众所周知的别名 About PARTITION BY,否则当查询在多个模式中运行时某些 SQL 服务器(如 2008),行排序不正确
-
您能否解释一下 CTE 正在尝试做什么,您要在这里完成什么,这将使我们更好地理解您的代码,目前您的开始日期和结束日期是相同的模式表,你能试着告诉我们你想在这里完成什么..
-
我认为@Surendra 很清楚,开始日期和结束日期与约会有关,所以他们不计算在这里,除了 MIN(StartDate) 是第一次发生的时间,这是给CTE的开始也是。我还在文中添加了额外的解释
标签: sql-server tsql common-table-expression datepart