2016-09-23 2 views
3

I haben die folgenden Daten in einer Tabelle:SQL Zählung aufeinanderfolgenden Reihen

|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  | 

I die folgenden Ergebnisse produzieren wollen, wo die maximale Anzahl von aufeinanderfolgenden Vorkommnissen, wo atended = ‚N‘ zurückgeführt wird:

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

Wie könnte dies in Oracle (oder ANSI) SQL getan werden? Vielen Dank!

Edit:

Bisher habe ich versucht:

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; 
+0

Gerade hinzugefügt, was ich bisher versucht haben, wenn es um die Verwendung kommt analytische Funktionen Ich bin mir immer noch nicht ganz sicher, wie es geht. – ubersnack

Antwort

6

Die Hauptarbeit ist in der faktorisierter Unterabfrage "Prep". Sie scheinen mit der analytischen Funktion etwas vertraut zu sein, aber das ist nicht genug. Diese Lösung verwendet die sogenannte "Tabibitosan" -Methode, um Gruppen von aufeinanderfolgenden Reihen mit der gleichen Charakteristik in einer oder mehreren Dimensionen zu erzeugen; In diesem Fall möchten Sie aufeinanderfolgende N Zeilen mit einer anderen Gruppe für jede Sequenz gruppieren. Dies geschieht mit einem Unterschied von zwei ROW_NUMBER() -Aufrufen - einer davon wird nur von einer Person partitioniert, der andere von einer Person und wird überwacht. Google "Tabibitosan", um mehr über die Idee zu lesen, wenn nötig.

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; 

OUTPUT:

PERSON_ID    MAX_CONSECUTIVE_ABSENCES 
---------- --------------------------------------- 
    10001          5 
    10002          3 
+0

hat perfekt funktioniert, danke! – ubersnack

0

Wenn Sie Oracle 12c verwenden könnten Sie MATCH_RECOGNIZE:

Daten:

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 
); 

Und query:

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; 

Ausgang:

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

EDIT:

Wie @mathguy darauf hingewiesen wie folgt umgeschrieben werden:

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; 
+0

Zu verschachtelt. Du brauchst nicht ALLE REIHEN PRO MATCH. Jedes Match sollte nur den "COUNT" für dieses Match zurückgeben. Dann sollte es keine WHERE-Klausel geben. Stattdessen sollte 'PATTERN'' a +' sein und die 'DEFINE' -Klausel sollte' DEFINE a AS besucht = 'N'' sein. Dies wird eine effizientere Lösung sein (wie durch Vergleichspläne gezeigt). – mathguy

+1

Ich habe bearbeitet, um das Wort 'FINAL' vor' COUNT (*) 'zu löschen. Wenn Sie 'all rows per match 'zurückgeben, verhält sich' count() 'wie eine analytische Funktion, es sei denn, Sie qualifizieren sie mit' final'. Aber wenn Sie ** eine ** Zeile pro Spiel (Standard) zurückgeben, gibt es kein "Laufen" vs. "Final". Es ist tatsächlich seltsam, dass Oracle keinen Syntaxfehler verursacht, wenn Sie 'final (something)' mit 'one row per count' verwenden; es ist nicht, aber "final" ist dort fehl am Platz. – mathguy