SQL:funzione aggregata e raggruppa per
Domanda
Consideriamo l'Oracolo emp
tavolo.Mi piacerebbe coinvolgere i dipendenti con lo stipendio più alto department = 20
E job = clerk
.Si supponga inoltre che non esista una colonna "empno" e che la chiave primaria coinvolga un numero di colonne.Puoi farlo con:
select * from scott.emp
where deptno = 20 and job = 'CLERK'
and sal = (select max(sal) from scott.emp
where deptno = 20 and job = 'CLERK')
Funziona, ma devo duplicare il test deptno = 20 e job = 'CLERK', cosa che vorrei evitare.Esiste un modo più elegante per scriverlo, magari usando a group by
?A proposito, se è importante, sto usando Oracle.
Soluzione
Quello che segue è un po' troppo ingegnerizzato, ma è un buon modello SQL per le query "top x".
SELECT
*
FROM
scott.emp
WHERE
(deptno,job,sal) IN
(SELECT
deptno,
job,
max(sal)
FROM
scott.emp
WHERE
deptno = 20
and job = 'CLERK'
GROUP BY
deptno,
job
)
Tieni inoltre presente che funzionerà in Oracle e Postgress (credo) ma non in MS SQL.Per qualcosa di simile in MS SQL vedi domanda Query SQL per ottenere l'ultimo prezzo
Altri suggerimenti
Se fossi sicuro del database di destinazione, opterei per la soluzione di Mark Nold, ma se mai desideri un SQL* indipendente dal dialetto, prova
SELECT *
FROM scott.emp e
WHERE e.deptno = 20
AND e.job = 'CLERK'
AND e.sal = (
SELECT MAX(e2.sal)
FROM scott.emp e2
WHERE e.deptno = e2.deptno
AND e.job = e2.job
)
*Credo che dovrebbe funzionare ovunque, ma non ho gli ambienti per testarlo.
In Oracle lo farei con una funzione analitica, quindi interrogheresti la tabella emp solo una volta:
SELECT *
FROM (SELECT e.*, MAX (sal) OVER () AS max_sal
FROM scott.emp e
WHERE deptno = 20
AND job = 'CLERK')
WHERE sal = max_sal
È più semplice, più facile da leggere e più efficiente.
Se desideri modificarlo per elencare queste informazioni per tutti i dipartimenti, dovrai utilizzare la clausola "PARTITION BY" in OVER:
SELECT *
FROM (SELECT e.*, MAX (sal) OVER (PARTITION BY deptno) AS max_sal
FROM scott.emp e
WHERE job = 'CLERK')
WHERE sal = max_sal
ORDER BY deptno
È fantastico!Non sapevo che potessi fare un confronto tra (x, y, z) con il risultato di un'istruzione SELECT.Funziona benissimo con Oracle.
Come nota a margine per gli altri lettori, nella query di cui sopra manca un "=" dopo "(deptno,job,sal)".Forse il formattatore Stack Overflow se lo è mangiato (?).
Ancora una volta, grazie Marco.
In Oracle puoi anche utilizzare l'istruzione EXISTS, che in alcuni casi è più veloce.
Per esempio...Selezionare il nome, numero da Cust in cui CUST in (selezionare Cust_id da Big_table) e inserito> sysdate -1 sarebbe lento.
Ma seleziona Nome, numero da CURS CDove esiste (seleziona cust_id da big_table DOVE cust_id=c.cust_id ) E inserito> sysdate -1 sarebbe molto veloce con una corretta indicizzazione.Puoi anche usarlo con più parametri.
Ci sono molte soluzioni.Potresti anche mantenere il layout della query originale semplicemente aggiungendo alias di tabella e unendoti ai nomi delle colonne, avresti comunque DEPTNO = 20 e JOB = 'CLERK' nella query una sola volta.
SELECT
*
FROM
scott.emp emptbl
WHERE
emptbl.DEPTNO = 20
AND emptbl.JOB = 'CLERK'
AND emptbl.SAL =
(
select
max(salmax.SAL)
from
scott.emp salmax
where
salmax.DEPTNO = emptbl.DEPTNO
AND salmax.JOB = emptbl.JOB
)
Si potrebbe anche notare che la parola chiave "ALL" può essere utilizzata per questo tipo di query che consentirebbero di rimuovere la funzione "MAX".
SELECT
*
FROM
scott.emp emptbl
WHERE
emptbl.DEPTNO = 20
AND emptbl.JOB = 'CLERK'
AND emptbl.SAL >= ALL
(
select
salmax.SAL
from
scott.emp salmax
where
salmax.DEPTNO = emptbl.DEPTNO
AND salmax.JOB = emptbl.JOB
)
Spero che questo aiuti e abbia senso.