Separazione dei risultati SQL in intervalli
-
03-07-2019 - |
Domanda
Vorrei prendere una semplice query su un elenco di membri che sono indicizzati da un numero e raggrupparli in "secchi" di uguale dimensione. Quindi la query di base è:
select my_members.member_index from my_members where my_members.active=1;
Supponi di recuperare 1000 numeri indice dei membri, ora voglio dividerli in 10 gruppi di dimensioni uguali per un indice membro massimo e minimo. Qualcosa del tipo:
Membri attivi da 0 a 400: 100 Membri attivi da 401 a 577: 100 ... Membri attivi dal 1584 al 1765: 100
Il meglio che ho potuto trovare è quello di interrogare ripetutamente il massimo (my_members.member_index) con un limite di rownum crescente:
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;
Soluzione
NTILE è la strada da percorrere: vale la pena leggere le funzioni analitiche in quanto possono semplificare enormemente il tuo SQL.
Piccolo commento sul codice originale: fare una restrizione di rownum prima un ORDER BY può produrre risultati negativi
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;
Prova quanto segue:
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;
Per ottenere il risultato che ti aspetti di dover fare
SELECT numval FROM
(SELECT numval FROM example_nums
ORDER BY numval DESC)
WHERE rownum < 5
(Nota: dietro le quinte, Oracle lo tradurrà in un ordinamento efficiente che contiene sempre e solo i "primi 4 elementi").
Altri suggerimenti
È semplice e molto più veloce usando la funzione analitica NTILE:
SELECT member_index, NTILE(10) OVER (ORDER BY member_index) FROM my_members;
Documentazione Oracle 10g: " NTILE è una funzione analitica. Divide un set di dati ordinato in un numero di bucket indicato da EXPR e assegna il numero di bucket appropriato a ciascuna riga. I bucket sono numerati da 1 a espresso "
Grazie per l'aiuto. Ci è voluto un po 'di tempo per elaborare tutto in una frase (per alcuni motivi che era anche un obiettivo), quindi ecco cosa mi è venuto in mente che sembra funzionare per me:
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;
Dammi i miei risultati in questo modo:
member_index ranger
2297683 1
2307055 2
2325667 3
2334819 4
2343982 5
2353325 6
2362247 7
6229146 8
8189767 9
26347329 10
Dai un'occhiata all'istruzione CASE in SQL e imposta un campo di gruppo basato sugli intervalli desiderati.