在没有 PL/SQL 的情况下有效地将行分组为剧集
Efficiently group rows into episodes without PL/SQL
我有一个数据集,每个实体每天有一条记录。每天只会有一个状态,但一个状态可以在几天内出现,也可以只在一天出现
我想做的是将这些组合成剧集。因此,每当 ID 更改、状态更改或下一条记录相隔超过一天时,我想将其视为新剧集。
我玩过分析函数,但虽然我可以按状态分组,但我会松开对每一天的跟踪(尽管我可能可以在每个状态一次通过)
我也可以在 PL/SQL 中执行此操作,但在我的测试中速度非常慢。我希望有一种方法可以将其作为查询执行或至少部分预处理作为查询,以便 pl/sql 循环更快。
ID DAY STATUS Comment
E0000000000054245349 27-Feb-16 24 Start
E0000000000054245349 28-Feb-16 24
E0000000000054245349 29-Feb-16 24
E0000000000054245349 1-Mar-16 24
E0000000000054245349 3-Mar-16 21 Gap & new status
E0000000000054245349 4-Mar-16 21
continuing daily
E0000000000054245349 12-Mar-16 21
E0000000000054245349 13-Mar-16 21
E0000000000054245349 14-Mar-16 21
E0000000000054245349 15-Mar-16 40 No gap, but new status
E0000000000054245349 16-Mar-16 40
E0000000000054245349 18-Mar-16 40 Gap, no new status
E0000000000054245349 19-Mar-16 40
E0000000000054245349 1-Jan-17 21 Gap & new status
E0000000000054245349 2-Jan-17 21
E0000000000054245349 3-Jan-17 21
E0000000000054245349 5-Jan-17 25 Gap, status and single day
我理想的数据集应该是这样的。如果它包含 1 天 before/after 记录的 previous/next 状态,则奖励积分,但如果需要,我总是可以通过后续查询获得它们
ID START END STATUS
E0000000000054245349 27-Feb-16 1-Mar-16 24
E0000000000054245349 3-Mar-16 14-Mar-16 21
E0000000000054245349 15-Mar-16 16-Mar-16 40
E0000000000054245349 18-Mar-16 19-Mar-16 40
E0000000000054245349 1-Jan-17 3-Jan-17 21
E0000000000054245349 5-Jan-17 5-Jan-17 25
这是一个缺口和孤岛问题 - 您可以尝试以下方法
select id, status,min(DAY) start,max(DAY) end
from
(
select *,island=row_number() over(partition by id order by day) -
row_number() over(partition by id, status order by day)
from tablename
)A group by id, status,island
您可以使用 Tabibitosan Method 轻松完成:
select id, min(day) mnd, max(day) mxd, status
from (
select day - row_number() over (partition by id order by day) grp, id, day, status
from t)
group by id, grp, status
order by id, grp;
这给出了期望的输出。如果 包含 1 天 before/after 记录的 previous/next 状态,我没有听懂该短语。在您的输出剧集之前/之后一天可能没有行。如果您想要上一行/下一行的状态,只需使用 lag()
和 lead ()
。但是如果你想要它只有如果新剧集是因为状态变化有条件地使用分析函数:
select id, mnd, mxd, status,
case mnd when lag(mxd) over (partition by id order by mxd) + 1
then lag(status) over (partition by id order by mxd)
end prev_status
from (select id, min(day) mnd, max(day) mxd, status
from (select day - row_number() over (partition by id order by day) grp,
id, day, status
from t)
group by id, grp, status)
order by id, mnd;
... lead()
.
相同
我有一个数据集,每个实体每天有一条记录。每天只会有一个状态,但一个状态可以在几天内出现,也可以只在一天出现
我想做的是将这些组合成剧集。因此,每当 ID 更改、状态更改或下一条记录相隔超过一天时,我想将其视为新剧集。
我玩过分析函数,但虽然我可以按状态分组,但我会松开对每一天的跟踪(尽管我可能可以在每个状态一次通过)
我也可以在 PL/SQL 中执行此操作,但在我的测试中速度非常慢。我希望有一种方法可以将其作为查询执行或至少部分预处理作为查询,以便 pl/sql 循环更快。
ID DAY STATUS Comment
E0000000000054245349 27-Feb-16 24 Start
E0000000000054245349 28-Feb-16 24
E0000000000054245349 29-Feb-16 24
E0000000000054245349 1-Mar-16 24
E0000000000054245349 3-Mar-16 21 Gap & new status
E0000000000054245349 4-Mar-16 21
continuing daily
E0000000000054245349 12-Mar-16 21
E0000000000054245349 13-Mar-16 21
E0000000000054245349 14-Mar-16 21
E0000000000054245349 15-Mar-16 40 No gap, but new status
E0000000000054245349 16-Mar-16 40
E0000000000054245349 18-Mar-16 40 Gap, no new status
E0000000000054245349 19-Mar-16 40
E0000000000054245349 1-Jan-17 21 Gap & new status
E0000000000054245349 2-Jan-17 21
E0000000000054245349 3-Jan-17 21
E0000000000054245349 5-Jan-17 25 Gap, status and single day
我理想的数据集应该是这样的。如果它包含 1 天 before/after 记录的 previous/next 状态,则奖励积分,但如果需要,我总是可以通过后续查询获得它们
ID START END STATUS
E0000000000054245349 27-Feb-16 1-Mar-16 24
E0000000000054245349 3-Mar-16 14-Mar-16 21
E0000000000054245349 15-Mar-16 16-Mar-16 40
E0000000000054245349 18-Mar-16 19-Mar-16 40
E0000000000054245349 1-Jan-17 3-Jan-17 21
E0000000000054245349 5-Jan-17 5-Jan-17 25
这是一个缺口和孤岛问题 - 您可以尝试以下方法
select id, status,min(DAY) start,max(DAY) end
from
(
select *,island=row_number() over(partition by id order by day) -
row_number() over(partition by id, status order by day)
from tablename
)A group by id, status,island
您可以使用 Tabibitosan Method 轻松完成:
select id, min(day) mnd, max(day) mxd, status
from (
select day - row_number() over (partition by id order by day) grp, id, day, status
from t)
group by id, grp, status
order by id, grp;
这给出了期望的输出。如果 包含 1 天 before/after 记录的 previous/next 状态,我没有听懂该短语。在您的输出剧集之前/之后一天可能没有行。如果您想要上一行/下一行的状态,只需使用 lag()
和 lead ()
。但是如果你想要它只有如果新剧集是因为状态变化有条件地使用分析函数:
select id, mnd, mxd, status,
case mnd when lag(mxd) over (partition by id order by mxd) + 1
then lag(status) over (partition by id order by mxd)
end prev_status
from (select id, min(day) mnd, max(day) mxd, status
from (select day - row_number() over (partition by id order by day) grp,
id, day, status
from t)
group by id, grp, status)
order by id, mnd;
... lead()
.