通过考虑展望日来分组日期
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 步 :) 这需要一些思考。以上是关于通过考虑展望日来分组日期的主要内容,如果未能解决你的问题,请参考以下文章