在没有 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;

dbfiddle demo

这给出了期望的输出。如果 包含 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().

相同