Funzione Oracle Analytic per il valore minimo nel raggruppamento
-
20-09-2019 - |
Domanda
Sono nuovo nel lavorare con le funzioni analitiche.
DEPT EMP SALARY ---- ----- ------ 10 MARY 100000 10 JOHN 200000 10 SCOTT 300000 20 BOB 100000 20 BETTY 200000 30 ALAN 100000 30 TOM 200000 30 JEFF 300000
Voglio il dipartimento e il dipendente con lo stipendio minimo.
I risultati dovrebbero essere simili a:
DEPT EMP SALARY ---- ----- ------ 10 MARY 100000 20 BOB 100000 30 ALAN 100000
MODIFICARE:Ecco l'SQL che ho (ma ovviamente non funziona perché vuole anche lo staff nella clausola group by):
SELECT dept, emp, MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) FROM mytable GROUP BY dept
Soluzione
Penso che la funzione Rank() non sia la strada da percorrere, per due motivi.
Innanzitutto è probabilmente meno efficiente di un metodo basato su Min().
La ragione di ciò è che la query deve mantenere un elenco ordinato di tutti gli stipendi per dipartimento mentre analizza i dati e la classifica verrà quindi assegnata in seguito rileggendo questo elenco.Ovviamente in assenza di indici che possano essere sfruttati a questo scopo, non è possibile assegnare un rango finché non è stato letto l'ultimo dato, e la manutenzione dell'elenco è costosa.
Pertanto, le prestazioni della funzione Rank() dipendono dal numero totale di elementi da sottoporre a scansione e, se il numero è sufficiente a far sì che l'ordinamento venga riversato sul disco, le prestazioni crolleranno.
Questo è probabilmente più efficiente:
select dept,
emp,
salary
from
(
SELECT dept,
emp,
salary,
Min(salary) Over (Partition By dept) min_salary
FROM mytable
)
where salary = min_salary
/
Questo metodo richiede solo che la query mantenga un singolo valore per dipartimento rispetto al valore minimo riscontrato finora.Se viene raggiunto un nuovo minimo, il valore esistente viene modificato, altrimenti il nuovo valore viene scartato.Il numero totale di elementi che devono essere mantenuti in memoria è correlato al numero di reparti, non al numero di righe scansionate.
Potrebbe essere che Oracle abbia un percorso di codice per riconoscere che il Rank non ha realmente bisogno di essere calcolato in questo caso, ma non ci scommetterei.
Il secondo motivo per cui Rank() non mi piace è che risponde semplicemente alla domanda sbagliata.La domanda non è "Quali record hanno lo stipendio che è il primo in classifica quando gli stipendi per dipartimento sono in ordine crescente", è "Quali record hanno lo stipendio che è il minimo per dipartimento".Questo, almeno per me, fa una grande differenza.
Altri suggerimenti
Credo che eri abbastanza vicino con la query originale. Quanto segue potrebbe correre e fare abbinare il vostro banco di prova:
SELECT dept,
MIN(emp) KEEP(DENSE_RANK FIRST ORDER BY salary, ROWID) AS emp,
MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary, ROWID) AS salary
FROM mytable
GROUP BY dept
A differenza del RANK () soluzioni, questo garantisce uno al massimo una riga per reparto. Ma che allude ad un problema: cosa succede in un reparto dove ci sono due dipendenti sul salario più basso? Le soluzioni RANK () restituirà sia i dipendenti - più di una riga per il reparto. Questa risposta sarà sceglierne uno arbitrariamente e assicurarsi che non c'è un solo per il reparto.
È possibile utilizzare la sintassi RANK()
. Ad esempio, questa query vi dirà dove un dipendente si colloca all'interno del loro reparto per quanto riguarda quanto grande il loro stipendio è:
SELECT
dept,
emp,
salary,
(RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept
FROM EMPLOYEES
È quindi possibile interrogare da questo dove salary_rank_within_dept = 1
:
SELECT * FROM
(
SELECT
dept,
emp,
salary,
(RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept
FROM EMPLOYEES
)
WHERE salary_rank_within_dept = 1
select e2.dept, e2.emp, e2.salary
from employee e2
where e2.salary = (select min(e1.salary) from employee e1)