Oracle
 sql >> Database >  >> RDS >> Oracle

Oracle SQL - Identifica gli intervalli di valori sequenziali

Questo è facile da fare con una tecnica chiamata Tabibitosan.

Ciò che fa questa tecnica è confrontare le posizioni delle righe di ciascun gruppo con l'insieme generale di righe, in modo da capire se le righe dello stesso gruppo sono una accanto all'altra o meno.

Ad esempio, con i tuoi dati di esempio, questo è simile a:

WITH your_table AS (SELECT 1 ID, 'Michael' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 2 ID, 'Alex' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 3 ID, 'Tom' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 4 ID, 'John' NAME, 'Sales' department FROM dual UNION ALL
                    SELECT 5 ID, 'Brad' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 6 ID, 'Leo' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 7 ID, 'Kevin' NAME, 'Production' department FROM dual)
-- end of mimicking your table with data in it. See the SQL below:
SELECT ID,
       NAME,
       department,
       row_number() OVER (ORDER BY ID) overall_rn,
       row_number() OVER (PARTITION BY department ORDER BY ID) department_rn,
       row_number() OVER (ORDER BY ID) - row_number() OVER (PARTITION BY department ORDER BY ID) grp
FROM   your_table;

        ID NAME    DEPARTMENT OVERALL_RN DEPARTMENT_RN        GRP
---------- ------- ---------- ---------- ------------- ----------
         1 Michael Marketing           1             1          0
         2 Alex    Marketing           2             2          0
         3 Tom     Marketing           3             3          0
         4 John    Sales               4             1          3
         5 Brad    Marketing           5             4          1
         6 Leo     Marketing           6             5          1
         7 Kevin   Production          7             1          6

Qui, ho assegnato a tutte le righe dell'intero set di dati un numero di riga in ordine crescente (il overall_rn colonna) e ho assegnato alle righe di ciascun reparto un numero di riga (il department_rn colonna), sempre in ordine crescente.

Ora che l'ho fatto, possiamo sottrarre uno dall'altro (il grp colonna).

Nota come il numero nella colonna grp rimane lo stesso per le righe deparment che sono una accanto all'altra, ma cambia ogni volta che c'è uno spazio vuoto.

Per esempio. per il reparto Marketing, le righe 1-3 sono una accanto all'altra e hanno grp =0, ma la 4a riga Marketing è in realtà sulla 5a riga del set di risultati complessivo, quindi ora ha un numero grp diverso. Poiché la 5a riga marketing si trova sulla 6a riga dell'insieme generale, ha lo stesso numero grp della 4a riga marketing, quindi sappiamo che sono uno accanto all'altro.

Una volta ottenute le informazioni su grp, è semplice eseguire un raggruppamento di query aggregato sia sul dipartimento che sulla nostra nuova colonna grp, utilizzando min e max per trovare gli ID di inizio e fine:

WITH your_table AS (SELECT 1 ID, 'Michael' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 2 ID, 'Alex' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 3 ID, 'Tom' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 4 ID, 'John' NAME, 'Sales' department FROM dual UNION ALL
                    SELECT 5 ID, 'Brad' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 6 ID, 'Leo' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 7 ID, 'Kevin' NAME, 'Production' department FROM dual)
-- end of mimicking your table with data in it. See the SQL below:
SELECT department,
       MIN(ID) start_id,
       MAX(ID) end_id
FROM   (SELECT ID,
               NAME,
               department,
               row_number() OVER (ORDER BY ID) - row_number() OVER (PARTITION BY department ORDER BY ID) grp
        FROM   your_table)
GROUP BY department, grp;

DEPARTMENT   START_ID     END_ID
---------- ---------- ----------
Marketing           1          3
Marketing           5          6
Sales               4          4
Production          7          7

NB, ho presupposto che gli spazi vuoti nelle colonne id non siano importanti (cioè se non ci fosse una riga per id =6 (quindi gli ID di Leo e Kevin erano rispettivamente 7 e 8), allora Leo e Brad apparirebbero ancora nello stesso gruppo, con id iniziale =5 e id finale =7.

Se le lacune nelle colonne id contano come un'indicazione di un nuovo gruppo, puoi semplicemente usare l'id per etichettare l'insieme generale di righe (cioè non c'è bisogno di calcolare il overall_rn; usa invece la colonna id).

Ciò significa che la tua query diventerebbe:

WITH your_table AS (SELECT 1 ID, 'Michael' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 2 ID, 'Alex' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 3 ID, 'Tom' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 4 ID, 'John' NAME, 'Sales' department FROM dual UNION ALL
                    SELECT 5 ID, 'Brad' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 7 ID, 'Leo' NAME, 'Marketing' department FROM dual UNION ALL
                    SELECT 8 ID, 'Kevin' NAME, 'Production' department FROM dual)
-- end of mimicking your table with data in it. See the SQL below:
SELECT department,
       MIN(ID) start_id,
       MAX(ID) end_id
FROM   (SELECT ID,
               NAME,
               department,
               ID - row_number() OVER (PARTITION BY department ORDER BY ID) grp
        FROM   your_table)
GROUP BY department, grp;

DEPARTMENT   START_ID     END_ID
---------- ---------- ----------
Marketing           1          3
Sales               4          4
Marketing           5          5
Marketing           7          7
Production          8          8