Separando los resultados de SQL en rangos
-
03-07-2019 - |
Pregunta
Me gustaría realizar una consulta simple en una lista de miembros indexados por un número y agruparlos en "grupos" de igual tamaño. Así que la consulta base es:
select my_members.member_index from my_members where my_members.active=1;
Digamos que recupero 1000 números de índice de miembros, ahora quiero dividirlos en 10 grupos de igual tamaño por un índice de miembros máximo y mínimo. Algo como:
Miembros activos de 0 a 400: 100 Miembros activos en 401 hasta 577: 100 ... Miembros activos desde 1584 hasta 1765: 100
Lo mejor que pude encontrar es consultar repetidamente el max (my_members.member_index) con un límite de rownum creciente:
for r in 1 .. 10 loop
select max(my_members.member_index)
into ranges(r)
from my_members
where my_members.active = 1
and rownum < top_row
order by my_members.member_index asc;
top_row := top_row + 100;
end loop;
Solución
NTILE es el camino a seguir, vale la pena leer sobre las funciones analíticas, ya que pueden simplificar enormemente su SQL.
Pequeño comentario sobre el código original: hacer una restricción de rownum antes un ORDER BY puede producir resultados adversos
for r in 1 .. 10 loop
select max(my_members.member_index)
into ranges(r)
from my_members
where my_members.active = 1
and rownum < top_row
order by my_members.member_index asc;
top_row := top_row + 100;
bucle final;
Prueba lo siguiente:
create table example_nums (numval number)
begin
for i in 1..100 loop
insert into example_nums values (i);
end loop;
end;
SELECT numval FROM example_nums
WHERE rownum < 5
ORDER BY numval DESC;
Para obtener el resultado que espera hacer
SELECT numval FROM
(SELECT numval FROM example_nums
ORDER BY numval DESC)
WHERE rownum < 5
(Nota: detrás de escena, Oracle lo convertirá en una ordenación eficiente que solo contiene los '4 elementos principales').
Otros consejos
Es simple y mucho más rápido usando la función analítica NTILE:
SELECT member_index, NTILE(10) OVER (ORDER BY member_index) FROM my_members;
Documentación de Oracle 10g: " NTILE es una función analítica. Divide un conjunto de datos ordenados en un número de grupos indicados por expr y asigna el número de grupo apropiado a cada fila. Los cubos están numerados del 1 al expr. & Quot;
Gracias por la ayuda. Tomó un tiempo convertirlo todo en una declaración (por ciertas razones, eso también era un objetivo), así que esto es lo que se me ocurrió y parece que me funciona:
select max(member_index), ranger
from (SELECT member_index,
CASE
WHEN rownum < sized THEN 1
WHEN rownum < sized*2 THEN 2
WHEN rownum < sized*3 THEN 3
WHEN rownum < sized*4 THEN 4
WHEN rownum < sized*5 THEN 5
WHEN rownum < sized*6 THEN 6
WHEN rownum < sized*7 THEN 7
WHEN rownum < sized*8 THEN 8
WHEN rownum < sized*9 THEN 9
ELSE 10
END ranger
from my_members,
(select count(*) / 10 sized
from my_members
where active = 1)
where active = 1
order by member_index)
group by ranger;
Dame mis resultados como este:
member_index ranger
2297683 1
2307055 2
2325667 3
2334819 4
2343982 5
2353325 6
2362247 7
6229146 8
8189767 9
26347329 10
Eche un vistazo a la declaración CASE en SQL y configure un campo de grupo basado en los rangos que desee.