Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Oracle SQL - Identifizieren Sie sequentielle Wertebereiche

Dies ist mit einer Technik namens Tabibitosan einfach zu bewerkstelligen.

Diese Technik vergleicht die Positionen der Zeilen jeder Gruppe mit dem Gesamtsatz von Zeilen, um herauszufinden, ob Zeilen in derselben Gruppe nebeneinander liegen oder nicht.

Mit Ihren Beispieldaten sieht das z. B. so aus:

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

Hier habe ich allen Zeilen über den gesamten Datensatz eine Zeilennummer in aufsteigender ID-Reihenfolge gegeben (die overall_rn Spalte), und ich habe den Zeilen in jeder Abteilung eine Zeilennummer gegeben (die department_rn Spalte), wieder in aufsteigender ID-Reihenfolge.

Jetzt, wo ich das getan habe, können wir einen vom anderen subtrahieren (die grp Spalte).

Beachten Sie, dass die Zahl in der Spalte grp für nebeneinander liegende Abteilungszeilen gleich bleibt, sich aber bei jeder Lücke ändert.

Z.B. Für die Marketingabteilung liegen die Zeilen 1-3 nebeneinander und haben grp =0, aber die 4. Marketingzeile befindet sich tatsächlich in der 5. Zeile des Gesamtergebnissatzes und hat jetzt eine andere grp-Nummer. Da sich die 5. Marketingreihe in der 6. Reihe des Gesamtsatzes befindet, hat sie dieselbe Grp-Nummer wie die 4. Marketingreihe, sodass wir wissen, dass sie nebeneinander liegen.

Sobald wir diese grp-Informationen haben, ist es einfach, eine aggregierte Abfragegruppierung sowohl für die Abteilung als auch für unsere neue grp-Spalte durchzuführen, wobei min und max verwendet werden, um die Start- und End-IDs zu finden:

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

Hinweis:Ich bin davon ausgegangen, dass Lücken in den ID-Spalten nicht wichtig sind (d. H. Wenn es keine Zeile für ID =6 gäbe (die IDs von Leo und Kevin waren also 7 bzw. 8), würden Leo und Brad immer noch in der selben erscheinen Gruppe, mit einer Start-ID =5 und End-ID =7.

Wenn Lücken in den id-Spalten als Hinweis auf eine neue Gruppe zählen, könnten Sie einfach die id verwenden, um den gesamten Satz von Zeilen zu kennzeichnen (d. h. es ist nicht erforderlich, den Gesamt_rn zu berechnen; verwenden Sie stattdessen einfach die id-Spalte).

Das bedeutet, dass Ihre Abfrage folgendermaßen aussehen würde:

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