计算具有重叠周期的值

Calculating values with overlapping periods

我有一个查询可以为 driver 的评估生成月度报告。一些 driver 可能几个月都没有报告。报告包含各种违规行为,其中一种违规行为是每月累计违规行为,并且每年 重新设置,这是在单独的查询 中完成的。一切工作都很好,除非客户向这部分添加了新要求。要求是将其从每年重置更改为重置一年中超过 180 天的任何违规行为。

这是一份报告的样本(一份 driver 报告):

RN                   ReportId             DriverId             StartDate               EndDate                 Level1Vio   Lv1YTD
-------------------- -------------------- -------------------- ----------------------- ----------------------- ----------- -----------
1                    64                   2073                 2020-10-21 00:00:00.000 2020-11-21 23:59:59.000 1           1
2                    65                   2073                 2020-11-24 05:13:04.133 2020-12-24 05:13:04.133 0           1
3                    67                   2073                 2020-12-23 06:53:52.870 2021-01-23 06:53:52.870 0           1
4                    68                   2073                 2021-01-22 06:33:43.127 2021-02-22 06:33:43.127 0           1
5                    69                   2073                 2021-02-23 04:02:58.680 2021-03-23 04:02:58.680 1           2
6                    70                   2073                 2021-03-22 23:39:33.570 2021-04-22 23:39:33.570 0           2
7                    71                   2073                 2021-04-22 00:28:35.230 2021-05-22 00:28:35.230 0           2
8                    72                   2073                 2021-05-22 15:46:21.767 2021-06-22 15:46:21.767 1           3
9                    73                   2073                 2021-06-25 06:42:02.130 2021-07-25 06:42:02.130 1           4
10                   76                   2073                 2021-07-23 17:42:01.533 2021-08-23 17:42:01.533 0           4

where working 的旧查询(生成上述样本):

SELECT 
    RN
,   ReportId
,   DriverId
,   StartDate
,   EndDate
,   Level1Vio
,   Lv1YTD = SUM(Level1Vio) OVER(PARTITION BY DriverId ORDER BY ReportId ROWS UNBOUNDED PRECEDING) 
FROM Report

例外结果应该是(对于新要求):

RN                   ReportId             DriverId             StartDate               EndDate                 Level1Vio   Lv1YTD
-------------------- -------------------- -------------------- ----------------------- ----------------------- ----------- -----------
1                    64                   2073                 2020-10-21 00:00:00.000 2020-11-21 23:59:59.000 1           1
2                    65                   2073                 2020-11-24 05:13:04.133 2020-12-24 05:13:04.133 0           1
3                    67                   2073                 2020-12-23 06:53:52.870 2021-01-23 06:53:52.870 0           1
4                    68                   2073                 2021-01-22 06:33:43.127 2021-02-22 06:33:43.127 0           1
5                    69                   2073                 2021-02-23 04:02:58.680 2021-03-23 04:02:58.680 1           2
6                    70                   2073                 2021-03-22 23:39:33.570 2021-04-22 23:39:33.570 0           2
7                    71                   2073                 2021-04-22 00:28:35.230 2021-05-22 00:28:35.230 0           1
8                    72                   2073                 2021-05-22 15:46:21.767 2021-06-22 15:46:21.767 1           2
9                    73                   2073                 2021-06-25 06:42:02.130 2021-07-25 06:42:02.130 1           3
10                   76                   2073                 2021-07-23 17:42:01.533 2021-08-23 17:42:01.533 0           3

SQL Fiddle

说明

如果您采取 ReportId = 64 (2020 年 11 月报告) driver 已注册违规,应在 180 天后删除,然后在ReportId = 69(2021 年 3 月报告) 再次违规。第一次违规应在 ReportId = 71(2021 年 5 月报告) 结束,第二次违规应在 ReportId = 76(2021 年 8 月报告)。该原则适用于每次违规。

尝试 实际上,我正在努力避免 cursorwhile 循环,但是我找不到一种方法来在一个查询 SELECT 下执行此操作,就像我在上面的查询中所做的那样。我试过 CTE 并加入,但没有成功。一切都迫使我使用循环,我尽量避免循环(因为性能问题)。我确信有一种方法可以用简单的 SELECT 来解决它,但我没有想法。因此,我们将不胜感激任何想法或解决方案。

我们可以只加入重叠 180 天的记录,而不是 window 函数。

已更新 fiddle:Working Test Case

SELECT t1.rn, t1.ReportId, t1.DriverId, t1.StartDate, t1.EndDate, t1.Level1Vio
     , SUM(t2.Level1Vio) AS Lv1YTD
  FROM Report AS t1
  JOIN Report AS t2
    ON t1.StartDate BETWEEN t2.StartDate AND DATEADD(day, 180, t2.StartDate)
   AND t1.DriverId = t2.DriverId
 GROUP BY t1.rn, t1.DriverId, t1.ReportId, t1.StartDate, t1.EndDate, t1.Level1Vio
 ORDER BY t1.StartDate
;

注意:我使用了从举报违规行为开始算起的 180 天。根据需要进行调整。

另请注意:我包含了一个使用 CTE 术语的版本。如果您希望更方便地过滤连接两侧感兴趣的行,将它们限制在特定年份,或者只是将该年份限制添加到连接逻辑或添加 WHERE 子句,则可以使用它。

有很多方法可以将年份逻辑全部包含在此查询中,而无需求助于每年生成单独的查询。