SQL计数连续的行

时间:2016-09-23 15:28:18

标签: sql oracle gaps-and-islands

我在表格中有以下数据:

|event_id    |starttime        |person_id|attended|
|------------|-----------------|---------|--------|
| 11512997-1 | 01-SEP-16 08:00 | 10001   | N      |
| 11512997-2 | 01-SEP-16 10:00 | 10001   | N      |
| 11512997-3 | 01-SEP-16 12:00 | 10001   | N      |
| 11512997-4 | 01-SEP-16 14:00 | 10001   | N      |
| 11512997-5 | 01-SEP-16 16:00 | 10001   | N      |
| 11512997-6 | 01-SEP-16 18:00 | 10001   | Y      |
| 11512997-7 | 02-SEP-16 08:00 | 10001   | N      |
| 11512997-1 | 01-SEP-16 08:00 | 10002   | N      |
| 11512997-2 | 01-SEP-16 10:00 | 10002   | N      |
| 11512997-3 | 01-SEP-16 12:00 | 10002   | N      |
| 11512997-4 | 01-SEP-16 14:00 | 10002   | Y      |
| 11512997-5 | 01-SEP-16 16:00 | 10002   | N      |
| 11512997-6 | 01-SEP-16 18:00 | 10002   | Y      |
| 11512997-7 | 02-SEP-16 08:00 | 10002   | Y      |

我想生成以下结果,其中返回atended ='N'的最大连续出现次数:

|person_id|consec_missed_max|
| 1001    | 5               |
| 1002    | 3               |

如何在Oracle(或ANSI)SQL中完成此操作?谢谢!

编辑:

到目前为止,我已经尝试过:

WITH t1 AS
(SELECT t.person_id,
    row_number() over(PARTITION BY t.person_id ORDER BY t.starttime) AS idx
    FROM the_table t
    WHERE t.attended = 'N'),
t2 AS
(SELECT person_id, MAX(idx) max_idx FROM t1 GROUP BY person_id)
SELECT t1.person_id, COUNT(1) ct
    FROM t1
    JOIN t2
    ON t1.person_id = t2.person_id
GROUP BY t1.person_id;

2 个答案:

答案 0 :(得分:6)

主要工作是在事实上的子查询" prep"。您似乎对分析功能有点熟悉,但这还不够。该解决方案使用所谓的" tabibitosan"在一个或多个维度中创建具有相同特征的连续行组的方法;在这种情况下,您希望将每个序列的连续N行分组为不同的组。这是通过两个ROW_NUMBER()调用的差异来完成的 - 一个是按人员划分的,另一个是按人划分并参加。 Google" tabibitosan"如果需要,可以阅读更多关于这个想法的信息。

with
     inputs ( event_id, starttime, person_id, attended ) as (
        select '11512997-1', to_date('01-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
        select '11512997-2', to_date('01-SEP-16 10:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all     
        select '11512997-3', to_date('01-SEP-16 12:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
        select '11512997-4', to_date('01-SEP-16 14:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
        select '11512997-5', to_date('01-SEP-16 16:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
        select '11512997-6', to_date('01-SEP-16 18:00', 'dd-MON-yy hh24:mi'), 10001, 'Y' from dual union all
        select '11512997-7', to_date('02-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
        select '11512997-1', to_date('01-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
        select '11512997-2', to_date('01-SEP-16 10:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
        select '11512997-3', to_date('01-SEP-16 12:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
        select '11512997-4', to_date('01-SEP-16 14:00', 'dd-MON-yy hh24:mi'), 10002, 'Y' from dual union all
        select '11512997-5', to_date('01-SEP-16 16:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
        select '11512997-6', to_date('01-SEP-16 18:00', 'dd-MON-yy hh24:mi'), 10002, 'Y' from dual union all
        select '11512997-7', to_date('02-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10002, 'Y' from dual
      ),
      prep ( starttime, person_id, attended, gp ) as (
        select starttime, person_id, attended,
               row_number() over (partition by person_id order by starttime) -
                   row_number() over (partition by person_id, attended 
                                      order by starttime)
        from   inputs
      ),
      counts ( person_id, consecutive_absences ) as (
        select person_id, count(*)
        from   prep
        where  attended = 'N'
        group by person_id, gp
     )
select person_id, max(consecutive_absences) as max_consecutive_absences
from   counts
group by person_id
order by person_id;

输出:

 PERSON_ID                MAX_CONSECUTIVE_ABSENCES
---------- ---------------------------------------
     10001                                       5
     10002                                       3

答案 1 :(得分:0)

如果您使用的是Oracle 12c,则可以使用MATCH_RECOGNIZE

数据:

CREATE TABLE data AS 
SELECT *
FROM (
with inputs ( event_id, starttime, person_id, attended ) as (
  select '11512997-1', to_date('01-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
  select '11512997-2', to_date('01-SEP-16 10:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all     
  select '11512997-3', to_date('01-SEP-16 12:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
  select '11512997-4', to_date('01-SEP-16 14:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
  select '11512997-5', to_date('01-SEP-16 16:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
  select '11512997-6', to_date('01-SEP-16 18:00', 'dd-MON-yy hh24:mi'), 10001, 'Y' from dual union all
  select '11512997-7', to_date('02-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10001, 'N' from dual union all
  select '11512997-1', to_date('01-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
  select '11512997-2', to_date('01-SEP-16 10:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
  select '11512997-3', to_date('01-SEP-16 12:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
  select '11512997-4', to_date('01-SEP-16 14:00', 'dd-MON-yy hh24:mi'), 10002, 'Y' from dual union all
  select '11512997-5', to_date('01-SEP-16 16:00', 'dd-MON-yy hh24:mi'), 10002, 'N' from dual union all
  select '11512997-6', to_date('01-SEP-16 18:00', 'dd-MON-yy hh24:mi'), 10002, 'Y' from dual union all
  select '11512997-7', to_date('02-SEP-16 08:00', 'dd-MON-yy hh24:mi'), 10002, 'Y' from dual
      )
SELECT * FROM inputs
);   

并查询:

SELECT PERSON_ID, MAX(LEN) AS MAX_ABSENCES_IN_ROW
FROM data
MATCH_RECOGNIZE (
   PARTITION BY PERSON_ID
   ORDER BY STARTTIME
   MEASURES FINAL COUNT(*) AS len
   ALL ROWS PER MATCH
   PATTERN(a b*)
   DEFINE b AS attended = a.attended
)
WHERE attended = 'N'
GROUP BY PERSON_ID;

输出:

"PERSON_ID","MAX_ABSENCES_IN_ROW"
10001,5
10002,3

修改

正如@mathguy指出的那样,它可以改写为:

SELECT PERSON_ID, MAX(LEN) AS MAX_ABSENCES_IN_ROW
FROM data
MATCH_RECOGNIZE (
   PARTITION BY PERSON_ID
   ORDER BY STARTTIME
   MEASURES COUNT(*) AS len
   PATTERN(a+)
   DEFINE a AS attended = 'N'
)
GROUP BY PERSON_ID;

<强> db<>fiddle demo