通过考虑展望日来分组日期

Posted

技术标签:

【中文标题】通过考虑展望日来分组日期【英文标题】:Group dates by considering look ahead days 【发布时间】:2021-01-31 08:11:13 【问题描述】:

我使用的是 SQL Server 2017。我有一个表 Requests,为简单起见,只有一列 RequestDate。例如,

RquestDate
4/11
4/12
4/13
4/16
4/18

我需要考虑前瞻天数,按 RequestDate 分组。如果前瞻日为 0,则结​​果应与原始表相同。

如果前瞻日为 1,这意味着当我查看 4/11 时,我需要检查 4/12 是否存在,如果存在,则将 4/12 归为 4/11。 结果是:

4/11 --it groups 4/12
4/13
4/16
4/18

如果前瞻日为 2,则在查看 4/11 时,会将 4/12、4/13 分组。 结果是:

4/11 -- group 4/12 and 4/13.
4/16 -- group 4/18

所以这个问题不同于典型的间隙和孤岛问题。因为当组日期时,可能会有差距,例如,当展望日为 2 时,4/16 组 4/17 和 4/18。

我尝试了一些方法,但找不到合适的解决方案。

【问题讨论】:

请向我们展示您的尝试。并且澄清一下,对于给定的查询运行,预测天数是否固定?我鼓励您使用 DDL+DML 添加一些示例数据,因为这样可以让人们更轻松地进行游戏和潜在的回答。 而且13虽然只比12多1,但肯定是开始了一个新组,不和11&12分组? 【参考方案1】:

递归公用表表达式可以工作。

    使用min() 函数选择开始请求日期。 使用与分组开始日期相同的日期。

第 1 步和第 2 步组成递归锚点/起始行。

    递归查找下一个请求日期。此日期高于上一个日期 (r.RequestDate > c.RequestDate) 并且没有其他行 遵循之前相同的标准 (not exists ... r2.RequestDate < r.RequestDate)。 如果当前请求日期(从步骤 3 开始)在前瞻间隔长度内,则保持分组开始日期 (then c.RequestGroupDate),否则在当前请求日期 (else r.RequestDate) 开始一个新组。

第 3 步和第 4 步构成 CTE 的递归部分。

    递归之后的每个请求日期作为对应的请求分组日期。 group by r.RequestGroupDate 子句将结果输出减少为不同的值。

样本数据

create table Requests
(
  RequestDate date
);

insert into Requests (RequestDate) values
('2021-04-11'),
('2021-04-12'),
('2021-04-13'),
('2021-04-16'),
('2021-04-18');

解决方案

declare @lookAhead int = 1; -- look ahead days parameter

with rcte as
(
  select min(r.RequestDate) as RequestDate,
         min(r.RequestDate) as RequestGroupDate
  from Requests r
union all
  select r.RequestDate,
         case
           when datediff(day, c.RequestGroupDate, r.RequestDate) <= @lookAhead
           then c.RequestGroupDate
           else r.RequestDate
         end
  from rcte c
  join Requests r
    on r.RequestDate > c.RequestDate
  where not exists ( select 'x'
                     from Requests r2
                     where r2.RequestDate > c.RequestDate
                       and r2.RequestDate < r.RequestDate )
)
select r.RequestGroupDate
from rcte r
group by r.RequestGroupDate;

结果

对于@lookAhead = 1

RequestGroupDate
----------------
2021-04-11
2021-04-13
2021-04-16
2021-04-18

对于@lookahead = 2

RequestGroupDate
----------------
2021-04-11
2021-04-16

Fiddle 了解实际情况。

【讨论】:

你能记录下它是如何工作的逻辑吗?这是一个很好的解决方案,但很难理解它的工作原理。 如果我理解正确,“where not exists”子句强制 CTE 逐行处理表。它就像一个光标从最旧的 RequestDate 移动到最新的 RequestDate。对于每一次移动,都携带上一个RequestGroupDate,判断当前行是否属于上一个ReuqestGroupDate。如果不是,它使用自己的 RequestDate 作为新的 RequestGroupDate。我唯一担心的是 SQL CTE Recursive 的最大深度是 32767。这是否意味着它无法处理超过 32767 行的表。我会测试它。 @Sander。 @DaleK,我添加了带有步骤的解决方案描述。 @FrankZhang,如果递归限制会造成问题,请再问一个问题并解释为什么这个解决方案不适合你。 @Sander 对此表示感谢 - 仍在努力解决第 3 步 :) 这需要一些思考。

以上是关于通过考虑展望日来分组日期的主要内容,如果未能解决你的问题,请参考以下文章

如何在不考虑时间的情况下按日期时间列分组

如何按日期分组,考虑时区和 DST?

如何通过 SQL Server 中的分组列根据日期列的最近 3 个月获取列平均值?

在 SQLAlchemy 中以特定格式按日期分组

Django通过对两个字段的查询来区分分组

Laravel:如何通过选择两列进行分组具有不同的值