To je snadné udělat pomocí techniky zvané Tabibitosan.
Tato technika umožňuje porovnat pozice řádků každé skupiny s celkovou sadou řádků, aby se zjistilo, zda jsou řádky ve stejné skupině vedle sebe nebo ne.
Například s vašimi vzorovými daty to vypadá takto:
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
Zde jsem všem řádkům v celé sadě dat přiřadil číslo řádku ve vzestupném pořadí id (overall_rn
sloupec) a řádkům v každém oddělení jsem přidělil číslo řádku (department_rn
sloupec), opět ve vzestupném pořadí id.
Nyní, když jsem to udělal, můžeme odečíst jeden od druhého (grp
sloupec).
Všimněte si, že číslo ve sloupci grp zůstává stejné pro řádky oddělení, které jsou vedle sebe, ale mění se pokaždé, když je mezera.
Např. pro marketingové oddělení jsou řádky 1-3 vedle sebe a mají grp =0, ale 4. řádek marketingu je ve skutečnosti na 5. řádku celkové sady výsledků, takže má nyní jiné číslo grp. Vzhledem k tomu, že 5. marketingová řada je na 6. řadě celé sady, má stejné grp číslo jako 4. marketingová řada, takže víme, že jsou vedle sebe.
Jakmile budeme mít informace o skupině grp, je to jednoduchá záležitost seskupení agregovaných dotazů na oddělení i náš nový sloupec skupiny grp pomocí min a max k nalezení počátečního a koncového ID:
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
POZN., předpokládal jsem, že mezery ve sloupcích id nejsou důležité (tj. pokud tam nebyl žádný řádek pro id =6 (takže Leo a Kevinovo id bylo 7 a 8), pak by se Leo a Brad stále objevovali ve stejném skupina s počátečním id =5 a koncovým id =7.
Pokud se mezery ve sloupcích id počítají jako označení nové skupiny, můžete k označení celé sady řádků použít pouze id (tj. není třeba počítat celkový_rn, stačí místo toho použít sloupec id).
To znamená, že váš dotaz bude:
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