我需要按id分组数据并找到max / min(date_from,date_to)。但如果有日期差距,它应该是新行。
我有以下数据:
SYS_ID ITEM_ID DATE_FROM DATE_TO
1 1 01.01.2019 20.01.2019
1 1 15.01.2019 10.02.2019
1 1 15.02.2019 20.02.2019
1 1 18.02.2019 10.03.2019
1 1 10.03.2019 22.03.2019
1 2 01.01.2019 10.01.2019
1 2 15.01.2019 25.01.2019
结果应该是:
SYS_ID ITEM_ID DATE_FROM DATE_TO
1 1 01.01.2019 10.02.2019
1 1 15.02.2019 22.03.2019
1 2 01.01.2019 10.01.2019
1 2 15.01.2019 25.01.2019
有没有办法在不使用游标的情况下执行此操作?
使用差距和岛屿方法
现场测试:http://sqlfiddle.com/#!18/0174b/3
with gap_detector as
(
select
sys_id, item_id,
date_from, date_to,
case when
lag(date_to)
over(partition by sys_id, item_id order by date_from) >= date_from
then
0
else
1
end as gap
from tbl
)
, grouper as
(
select
sys_id, item_id,
date_from, date_to,
sum(gap) over(partition by sys_id, item_id order by date_from) as grp
from gap_detector
)
select
sys_id, item_id,
min(date_from) as date_from,
max(date_to) as date_to
from grouper
group by sys_id, item_id, grp
输出:
| sys_id | item_id | date_from | date_to |
|--------|---------|------------|------------|
| 1 | 1 | 2019-01-01 | 2019-02-10 |
| 1 | 1 | 2019-02-15 | 2019-03-22 |
| 1 | 2 | 2019-01-01 | 2019-01-10 |
| 1 | 2 | 2019-01-15 | 2019-01-25 |
首先,我们需要检测前一行的date_to(使用lag
)是否与当前的date_from重叠。
注意,我们有独立的date_from集合,即sys_id
+ item_id
组合的前一行(例如,1,1)与另一个sys_id
+ item_id
组合(例如1,2)不重叠。所以1,2
的第一个前一个date_to不是March 22, 2019
,而是NULL
。我们可以通过划分它们来正确识别每个sys_id
+ item_id
组合的前一行,即partition by sys_id, item_id
。
有了这就是说我们如何识别前一行的date_to是否与当前的date_from重叠:
gap
)前一行的当前行,我们可以通过赋予它值1来实现此目的。后来为什么我们需要1和0。现场测试:http://sqlfiddle.com/#!18/0174b/7
with gap_detector as
(
select
sys_id, item_id,
date_from, date_to,
case when
lag(date_to)
over(partition by sys_id, item_id order by date_from) >= date_from
then
0
else
1
end as gap
from tbl
)
select *
from gap_detector
order by sys_id, item_id, date_from
输出:
| sys_id | item_id | date_from | date_to | gap |
|--------|---------|------------|------------|-----|
| 1 | 1 | 2019-01-01 | 2019-01-20 | 1 |
| 1 | 1 | 2019-01-15 | 2019-02-10 | 0 |
| 1 | 1 | 2019-02-15 | 2019-02-20 | 1 |
| 1 | 1 | 2019-02-18 | 2019-03-10 | 0 |
| 1 | 1 | 2019-03-10 | 2019-03-22 | 0 |
| 1 | 2 | 2019-01-01 | 2019-01-10 | 1 |
| 1 | 2 | 2019-01-15 | 2019-01-25 | 1 |
下一步是通过在间隙标记(1和0)上运行总计来对彼此属于的岛进行分组。通过在sum(gap)
+ sys_id
组合窗口上执行item_id
来完成总计。
sys_id
+ item_id
组合的每个窗口都可以通过对它们进行partition
独立操作,即partition by sys_id, item_id
现场测试:http://sqlfiddle.com/#!18/0174b/12
with gap_detector as
(
select
sys_id, item_id,
date_from, date_to,
case when
lag(date_to)
over(partition by sys_id, item_id order by date_from) >= date_from
then
0
else
1
end as gap
from tbl
)
, grouper as
(
select
sys_id, item_id,
date_from, date_to,
gap,
sum(gap) over(partition by sys_id, item_id order by date_from) as grp
from gap_detector
)
select sys_id, item_id, date_from, date_to, gap, grp
from grouper
输出:
| sys_id | item_id | date_from | date_to | gap | grp |
|--------|---------|------------|------------|-----|-----|
| 1 | 1 | 2019-01-01 | 2019-01-20 | 1 | 1 |
| 1 | 1 | 2019-01-15 | 2019-02-10 | 0 | 1 |
| 1 | 1 | 2019-02-15 | 2019-02-20 | 1 | 2 |
| 1 | 1 | 2019-02-18 | 2019-03-10 | 0 | 2 |
| 1 | 1 | 2019-03-10 | 2019-03-22 | 0 | 2 |
| 1 | 2 | 2019-01-01 | 2019-01-10 | 1 | 1 |
| 1 | 2 | 2019-01-15 | 2019-01-25 | 1 | 2 |
最后,既然我们能够确定哪些岛屿彼此属于(由grp
表示),那么只需要在这些group by
标记上进行grp
,以确定何时在岛屿的每个群组(grp
)上开始date_from和date_to。
现场测试:http://sqlfiddle.com/#!18/0174b/13
select
sys_id, item_id,
min(date_from) as date_from,
max(date_to) as date_to
from grouper
group by sys_id, item_id, grp
输出:
| sys_id | item_id | date_from | date_to |
|--------|---------|------------|------------|
| 1 | 1 | 2019-01-01 | 2019-02-10 |
| 1 | 1 | 2019-02-15 | 2019-03-22 |
| 1 | 2 | 2019-01-01 | 2019-01-10 |
| 1 | 2 | 2019-01-15 | 2019-01-25 |