Pan*_*tea 0 sql-server gaps-and-islands sql-server-2016
我有一张表格,如下所示:
create table z_test_duration
( Days date,
Status char(8)
);
Run Code Online (Sandbox Code Playgroud)
样本数据如下:
天 | 地位 |
---|---|
2022 年 1 月 1 日 | 在 |
2022 年 1 月 2 日 | 在 |
2022 年 1 月 3 日 | 在 |
2022 年 1 月 4 日 | 离开 |
2022 年 1 月 5 日 | 在 |
2022 年 1 月 6 日 | 离开 |
2022 年 1 月 7 日 | 在 |
2022 年 1 月 8 日 | 在 |
2022 年 1 月 9 日 | 离开 |
想要的结果是这样的
时间到了 | OFF_DATE | COUNT_OF_ACTIVE_DAYS 个 |
---|---|---|
2022 年 1 月 1 日 | 2022 年 1 月 4 日 | 3 |
2022 年 1 月 5 日 | 2022 年 1 月 6 日 | 1 |
2022 年 1 月 7 日 | 2022 年 1 月 9 日 | 2 |
到目前为止我的解决方案是这样的:
select min(days) on_date,
off_day off_date,
off_day - min(days) cnt
from (select t1.off_day,
t1.prev_offday,
t2.days
from (
select t.days off_day,
nvl(lag(t.days, 1) over(order by t.days),convert(datetime, '1/1/2022') - 100) prev_offday
from z_test_duration t
where t.status = 'off'
) t1
inner join z_test_duration t2
on t2.days > t1.prev_offday
and t2.days < t1.off_day)
group by off_day;
Run Code Online (Sandbox Code Playgroud)
我在想如果有更好的方法来解决这个问题,如果您分享解决这个问题的方法,我将不胜感激。
提前致谢。
这是一个“孤岛”问题。
一种流行且有效的解决方案是按所需的顺序对行进行编号。当序列出现间隙时,排序列和行号之间的差异也会跳跃。
让我们一步步来看。首先,编号:
SELECT
Z.*,
Seq = Z.[Days], -- ordering column
rn = ROW_NUMBER() OVER (ORDER BY Z.[Days]) -- numbering
FROM dbo.z_test_duration AS Z
WHERE Z.[Status] = 'on';
Run Code Online (Sandbox Code Playgroud)
天 | 地位 | 序列 | rn |
---|---|---|---|
2022-01-01 | 在 | 2022-01-01 | 1 |
2022-01-02 | 在 | 2022-01-02 | 2 |
2022-01-03 | 在 | 2022-01-03 | 3 |
2022-01-05 | 在 | 2022-01-05 | 4 |
2022-01-07 | 在 | 2022-01-07 | 5 |
2022-01-08 | 在 | 2022-01-08 | 6 |
请注意,Seq
值以相同的速率增加,rn
直到出现间隙。rn
通过减去该值,我们可以更清楚地看到这一点Seq
。
这里唯一稍微复杂的是Seq
a date
,所以我们需要在减法之前将其转换为数字。我在这里使用了该DATEDIFF
函数,但是任何将日期转换为数字的一致方法都可以。
SELECT
Z.*,
Seq = Z.[Days],
diff =
DATEDIFF(DAY, '2022-01-01', Z.[Days]) -
ROW_NUMBER() OVER (
ORDER BY Z.[Days])
FROM dbo.z_test_duration AS Z
WHERE Z.[Status] = 'on';
Run Code Online (Sandbox Code Playgroud)
天 | 地位 | 序列 | 差异 |
---|---|---|---|
2022-01-01 | 在 | 2022-01-01 | -1 |
2022-01-02 | 在 | 2022-01-02 | -1 |
2022-01-03 | 在 | 2022-01-03 | -1 |
2022-01-05 | 在 | 2022-01-05 | 0 |
2022-01-07 | 在 | 2022-01-07 | 1 |
2022-01-08 | 在 | 2022-01-08 | 1 |
组中每个连续元素的值diff
都相同。
现在我们知道了如何分组,最终的查询直接如下:
SELECT
ON_DATE = MIN(G.Seq),
OFF_DATE = DATEADD(DAY, 1, MAX(G.Seq)),
COUNT_OF_ACTIVE_DAYS = 1 + DATEDIFF(DAY, MIN(G.Seq), MAX(G.Seq))
FROM
(
SELECT
Z.*,
Seq = Z.[Days],
grp =
DATEDIFF(DAY, '2022-01-01', Z.[Days]) -
ROW_NUMBER() OVER (
ORDER BY Z.[Days])
FROM dbo.z_test_duration AS Z
WHERE Z.[Status] = 'on'
) AS G
GROUP BY G.grp;
Run Code Online (Sandbox Code Playgroud)
时间到了 | OFF_DATE | COUNT_OF_ACTIVE_DAYS 个 |
---|---|---|
2022-01-01 | 2022-01-04 | 3 |
2022-01-05 | 2022-01-06 | 1 |
2022-01-07 | 2022-01-09 | 2 |
归档时间: |
|
查看次数: |
2752 次 |
最近记录: |