【问题标题】:T-SQL CTE, calculation on Previous Row valueT-SQL CTE,上一行值的计算
【发布时间】:2016-10-28 14:57:44
【问题描述】:

我有一个 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 和其他功能。

【问题讨论】:

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


    【解决方案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
    

    【讨论】:

    • 哇,你真的扭曲了逻辑。我只是使用执行计划运行您的查询,它运行良好。谢谢!
    • 还有一个问题,如果我有一个额外的列来指定我必须跳过的周数怎么办?当我必须连续每周计算 n 天时,您的脚本会起作用,如果我想在“每 3 周”完成同样的任务怎么办?因为 CTE 没有从表中获取参数
    • 添加了另一个答案以支持 SKIPS 查看评论
    • 对不起@John Cappelletti,我在跳过时看不到它
    • 不。你会看到的。只是验证结果
    【解决方案2】:

    为每个添加了一个字段。所以 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)
    

    【讨论】:

    • 它就像一个魅力,今天你教我如何在不使用 LOOKUP 日历的情况下使用 DATEADD 进行认真迭代。谢谢!
    • 乐于助人。干杯
    猜你喜欢
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-12
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多