T-SQL CTE,计算上一行值

时间:2016-06-26 14:46:23

标签: sql-server date common-table-expression

我有一个CTE,它根据模式以下列方式返回一组行:

模式表从开始日期开始为下一个星期一创建100行

|                                        |
|--Id--|--Start Date--|--Count--|--Days--|
|                                        |
|--AB--|  01-01-2000  |   100   | Monday |
|                                        |

我有功能,通过传递开始日期计算下一个发生日

dbo.fx_get_next_occurrence(@startDate DATETIME, @days VARCHAR)

为了生成行,我使用CTE如下:

WITH Occurrences_CTE(Num, Id, StartDate)
AS
(
    SELECT
        SP.n
        ,F.Id
        ,dbo.fx_get_next_occurrence(F.Days, F.StartDate)
    FROM
        #RecurrencePattern AS F 
        INNER JOIN
        dbo.Numbers AS SP
        ON SP.n between 1 and F.[Count]
)

dbo.numbers 是一个包含增量数字列表的表。

现在,当我运行SELECT当然,CTE总是返回相同的"下一个日期"因为Date永远不会增加,所以这个SELECT:

SELECT 
    CTE.Id, CTE.Num, CTE.StartDate
FROM 
    Occurrences_CTE CTE

生成如下结果:

|                             |
|--Id--|--Num--|--Start Date--|
|                             |
|--AB--|--001--|--01-03-2000--|
|--AB--|--002--|--01-03-2000--|
|--AB--|--003--|--01-03-2000--|
|--AB--|--004--|--01-03-2000--|
|--AB--|--005--|--01-03-2000--|

现在,尽管函数按预期工作,但我如何从CTE获取之前计算的日期行,以便我可以将此日期传递给我的函数从前一个开始到下一个可用的日期?将CTE加入到自身,然后呢?

在SQL 2012和2014上午,我可以使用LAG和其他功能。

2 个答案:

答案 0 :(得分:1)

也许你可以稍微简化一下。

我无法与您的UDF对话,但可能没有必要,因为您很快就会看到。 cteKeyDate产生的不仅仅是必要的,但是当您加入数据时会过滤结果。

您可能还注意到我在您的样本数据中添加了另一行,该行发生在星期一和星期五,但只计数为50

Declare @Table table (id varchar(25),StartDate Date,Count int,Days varchar(25))
Insert into @Table values
('AB','2000-01-01',100,'Monday'),
('CD','2000-01-01',50,'Monday,Friday')


Declare @MinDate Date = (Select min(StartDate) from @Table)
Declare @MaxCntr Int  = (Select max(Count) from @Table)
Declare @MaxDate Date = DateAdd(wk,@MaxCntr+10,@MinDate)

;With cteKeyDate As (
    Select KeyDate = @MinDate,KeyDOW = DateName(WEEKDAY,@MinDate)
    Union All
    Select KeyDate =  DateAdd(DD, 1, df.KeyDate) ,KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyDate))
    From cteKeyDate DF
    Where DF.KeyDate <= @MaxDate
) 
Select * 
 From (
        Select A.ID
              ,A.StartDate
              ,A.Count
              ,A.Days 
              ,KeyDate
              ,KeyDow
              ,RowNr = Row_Number() over (Partition By A.ID Order By B.KeyDate)
         from @Table A
         Join cteKeyDate B on B.KeyDate>=A.StartDate and Charindex(KeyDOW,Days)>0 
      ) Final
 Where RowNr<=Count
 Option (maxrecursion 32767)

返回 - 我显示了所有字段,因此您可以更好地可视化结果

ID  StartDate   Count   Days            KeyDate     KeyDow  RowNr
AB  2000-01-01  100     Monday          2000-01-03  Monday  1
AB  2000-01-01  100     Monday          2000-01-10  Monday  2
AB  2000-01-01  100     Monday          2000-01-17  Monday  3
AB  2000-01-01  100     Monday          2000-01-24  Monday  4
... 
AB  2000-01-01  100     Monday          2001-11-05  Monday  97
AB  2000-01-01  100     Monday          2001-11-12  Monday  98
AB  2000-01-01  100     Monday          2001-11-19  Monday  99
AB  2000-01-01  100     Monday          2001-11-26  Monday  100
CD  2000-01-01  50      Monday,Friday   2000-01-03  Monday  1
CD  2000-01-01  50      Monday,Friday   2000-01-07  Friday  2
CD  2000-01-01  50      Monday,Friday   2000-01-10  Monday  3
.....
CD  2000-01-01  50      Monday,Friday   2000-06-16  Friday  48
CD  2000-01-01  50      Monday,Friday   2000-06-19  Monday  49
CD  2000-01-01  50      Monday,Friday   2000-06-23  Friday  50

答案 1 :(得分:1)

为Every添加了一个字段。所以1 =每周,2每隔一周,依此类推。

以此为旋转

Declare @Table table (id varchar(25),StartDate Date,Count int,Days varchar(25),Every int)
Insert into @Table values
('AB','2000-01-01',50,'Monday',2),
('CD','2000-01-01',50,'Monday,Friday',2)


Declare @MinDate Date = (Select min(StartDate) from @Table)
Declare @MaxCntr Int  = (Select max(Count) from @Table)
Declare @MaxDate Date = DateAdd(wk,@MaxCntr+10,@MinDate)

;With cteKeyDate As (
    Select KeyDate = @MinDate,KeyDOW = DateName(WEEKDAY,@MinDate),KeyWeek = datepart(WK,@MinDate)
    Union All
    Select KeyDate =  DateAdd(DD, 1, df.KeyDate) ,KeyDOW = DateName(WEEKDAY,DateAdd(DD, 1, df.KeyDate)), KeyWeek= DatePart(WK,DateAdd(DD, 1, df.KeyDate))
    From cteKeyDate DF
    Where DF.KeyDate <= @MaxDate
) 
Select * 
 From (
        Select A.ID
              ,A.StartDate
              ,A.Count
              ,A.Days 
              ,A.Every
              ,KeyDate
              ,KeyDow
              ,KeyWeek
              ,RowNr = Row_Number() over (Partition By A.ID Order By B.KeyDate)
         from @Table A
         Join cteKeyDate B on B.KeyDate>=A.StartDate and Charindex(KeyDOW,Days)>0 
      ) Final
 Where RowNr<=Count and (KeyWeek) % (Every) = 0 
 Option (maxrecursion 32767)