Dette er nemt at gøre med en teknik kaldet Tabibitosan.
Hvad denne teknik gør, er at sammenligne positionerne af hver gruppes rækker med det samlede sæt af rækker for at finde ud af, om rækker i samme gruppe er ved siden af hinanden eller ej.
Med dine eksempeldata ser dette f.eks. sådan ud:
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
Her har jeg givet alle rækkerne på tværs af hele datasættet et rækkenummer i stigende id-rækkefølge (overall_rn
kolonne), og jeg har givet rækkerne i hver afdeling et rækkenummer (department_rn
kolonne), igen i stigende id-rækkefølge.
Nu hvor jeg har gjort det, kan vi trække det ene fra det andet (grp
kolonne).
Bemærk, hvordan tallet i grp-kolonnen forbliver det samme for afdelingsrækker, der er ved siden af hinanden, men det ændrer sig, hver gang der er et hul.
For eksempel. for marketingafdelingen ligger række 1-3 ved siden af hinanden og har grp =0, men 4. marketingrække er faktisk på 5. række i det samlede resultatsæt, så den har nu et andet grp-nummer. Da 5. marketingrække er på 6. række i det samlede sæt, har den samme grp-nummer som 4. marketingrække, så vi ved, at de er ved siden af hinanden.
Når vi først har disse grp-oplysninger, er det et simpelt spørgsmål om at lave en samlet forespørgselsgruppering på både afdelingen og vores nye grp-kolonne, ved at bruge min og max til at finde start- og slut-id'erne:
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
N.B., jeg har antaget, at huller i id-kolonnerne ikke er vigtige (dvs. hvis der ikke var nogen række for id =6 (så Leo og Kevins id var henholdsvis 7 og 8), så ville Leo og Brad stadig optræde i det samme gruppe, med et start-id =5 og slut-id =7.
Hvis huller i id-kolonnerne tæller som at angive en ny gruppe, så kan du bare bruge id'et til at mærke det overordnede sæt af rækker (dvs. det er ikke nødvendigt at beregne overall_rn; brug blot id-kolonnen i stedet).
Det betyder, at din forespørgsel bliver:
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