sql >> Base de Datos >  >> RDS >> Oracle

Oracle SQL:identificar rangos de valores secuenciales

Esto es fácil de hacer con una técnica llamada Tabibitosan.

Lo que hace esta técnica es comparar las posiciones de las filas de cada grupo con el conjunto general de filas, para determinar si las filas del mismo grupo están una al lado de la otra o no.

Por ejemplo, con sus datos de ejemplo, esto se ve así:

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

Aquí, le he dado a todas las filas en todo el conjunto de datos un número de fila en orden de identificación ascendente (el overall_rn columna), y he dado a las filas en cada departamento un número de fila (el department_rn columna), de nuevo en orden de identificación ascendente.

Ahora que lo he hecho, podemos restar uno del otro (el grp columna).

Observe cómo el número en la columna grp permanece igual para las filas de departamento que están una al lado de la otra, pero cambia cada vez que hay un espacio.

P.ej. para el departamento de Marketing, las filas 1-3 están una al lado de la otra y tienen grp =0, pero la 4.ª fila de Marketing está en realidad en la 5.ª fila del conjunto de resultados generales, por lo que ahora tiene un número de grp diferente. Dado que la quinta fila de marketing está en la sexta fila del conjunto general, tiene el mismo número de grupo que la cuarta fila de marketing, por lo que sabemos que están uno al lado del otro.

Una vez que tenemos esa información de grp, es una simple cuestión de hacer una agrupación de consulta agregada tanto en el departamento como en nuestra nueva columna de grp, usando min y max para encontrar las identificaciones de inicio y fin:

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., asumí que los espacios en las columnas de id no son importantes (es decir, si no hubiera una fila para id =6 (por lo que los id de Leo y Kevin eran 7 y 8 respectivamente), entonces Leo y Brad seguirían apareciendo en el mismo grupo, con un ID inicial =5 y un ID final =7.

Si los espacios en las columnas de identificación cuentan como una indicación de un nuevo grupo, entonces podría usar la identificación para etiquetar el conjunto general de filas (es decir, no es necesario calcular el valor general_rn; simplemente use la columna de identificación en su lugar).

Eso significa que su consulta se convertiría en:

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