Jest to łatwe dzięki technice zwanej Tabibitosan.
Ta technika polega na porównaniu pozycji wierszy każdej grupy z całym zestawem wierszy, aby sprawdzić, czy wiersze w tej samej grupie są obok siebie, czy nie.
Np. z przykładowymi danymi wygląda to tak:
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
Tutaj nadałem wszystkim wierszom w całym zestawie danych numer wiersza w rosnącej kolejności identyfikatorów (overall_rn
kolumna) i nadałem wierszom w każdym dziale numer wiersza (department_rn
kolumna), ponownie w rosnącej kolejności identyfikatorów.
Teraz, gdy to zrobiłem, możemy odjąć jedno od drugiego (grp
kolumna).
Zwróć uwagę, że liczba w kolumnie grp pozostaje taka sama dla wierszy oddziałów znajdujących się obok siebie, ale zmienia się za każdym razem, gdy pojawia się luka.
Np. w przypadku działu marketingu wiersze 1-3 znajdują się obok siebie i mają grp =0, ale czwarty wiersz marketingu znajduje się w rzeczywistości w piątym wierszu ogólnego zestawu wyników, więc ma teraz inny numer grp. Ponieważ piąty wiersz marketingowy znajduje się w szóstym rzędzie całego zestawu, ma ten sam numer grupy, co czwarty wiersz marketingowy, więc wiemy, że są one obok siebie.
Gdy mamy już te informacje grp, wystarczy wykonać grupowanie zagregowanych zapytań zarówno w dziale, jak i w naszej nowej kolumnie grp, używając min i max do znalezienia identyfikatorów początkowych i końcowych:
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
Uwaga, założyłem, że luki w kolumnach id nie są ważne (tzn. gdyby nie było wiersza dla id =6 (więc id Leo i Kevina wynosiły odpowiednio 7 i 8), to Leo i Brad nadal pojawiliby się w tym samym grupa, z identyfikatorem początkowym =5 i identyfikatorem końcowym =7.
Jeśli przerwy w kolumnach id liczą się jako wskazujące na nową grupę, możesz po prostu użyć tego identyfikatora do oznaczenia całego zestawu wierszy (tzn. nie trzeba obliczać ogólnego_rn; zamiast tego użyj kolumny id).
Oznacza to, że Twoje zapytanie wyglądałoby następująco:
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