2008-09-09 6 views
3

Envisagez la table Oracle emp. Je voudrais obtenir les employés avec le meilleur salaire avec department = 20 et job = clerk. Supposons également qu'il n'y a pas de colonne "empno" et que la clé primaire implique un certain nombre de colonnes. Vous pouvez le faire avec:SQL: fonction d'agrégat et groupe par

select * from scott.emp 
where deptno = 20 and job = 'CLERK' 
and sal = (select max(sal) from scott.emp 
      where deptno = 20 and job = 'CLERK') 

Cela fonctionne, mais je dois reproduire le test deptno = 20 et travail = « COMMIS », que je voudrais éviter. Existe-t-il un moyen plus élégant d'écrire ceci, peut-être en utilisant un group by? BTW, si cela est important, j'utilise Oracle.

Répondre

3

Ce qui suit est légèrement sur-conçu, mais constitue un bon modèle SQL pour les requêtes "top x". Notez également que cela fonctionnera dans Oracle et Postgress (je pense) mais pas MS SQL. Pour quelque chose de similaire dans MS SQL, voir la question SQL Query to get latest price

0

C'est génial! Je ne savais pas que vous pouviez faire une comparaison de (x, y, z) avec le résultat d'une instruction SELECT. Cela fonctionne très bien avec Oracle.

Comme note complémentaire pour les autres lecteurs, la requête ci-dessus manque un "=" après "(deptno, job, sal)". Peut-être que le formateur Stack Overflow l'a mangé (?).

Encore une fois, merci Mark.

+0

Pas de problème, j'ai édité la réponse :) ça devrait être un IN –

2

Si j'étais certain de la base de données ciblée je partirais avec la solution de Mark Nold, mais si vous voulez toujours un dialecte agnostique SQL *, essayez

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 
) 

* Je crois que cela devrait fonctionner partout, mais je n'ont pas les environnements pour le tester.

0

Dans Oracle, vous pouvez également utiliser l'instruction EXISTS, qui dans certains cas est plus rapide.

Par exemple ... SELECT nom, numéro DE Cust OÙ EN Cust (SELECT cust_id DE big_table) ET entrée> SYSDATE -1 serait lente.

mais SELECT nom, numéro DE Cust c OÙ EXISTE (SELECT cust_id DE big_table OU cust_id = c.cust_id) et est entré> SYSDATE -1 serait très rapide avec une bonne indexation. Vous pouvez également utiliser ceci avec plusieurs paramètres.

0

Il existe de nombreuses solutions. Vous pouvez également conserver votre mise en page de requête originale en ajoutant simplement des alias de table et en vous unissant sur les noms de colonnes, vous n'auriez toujours que DEPTNO = 20 et JOB = 'CLERK' dans la requête une fois.

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 
    ) 

Il peut aussi noter que le mot clé « ALL » peut être utilisé pour ces types de requêtes qui vous permettent de supprimer la fonction « 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 
    ) 

J'espère que cela aide et a du sens.

1

Dans Oracle, je le ferais avec une fonction analytique, de sorte que vous vouliez seulement interroger la table emp fois:

SELECT * 
    FROM (SELECT e.*, MAX (sal) OVER() AS max_sal 
      FROM scott.emp e 
     WHERE deptno = 20 
      AND job = 'CLERK') 
WHERE sal = max_sal 

Il est plus simple, plus facile à lire et plus efficace.

Si vous souhaitez modifier la liste liste ces informations pour tous les ministères, alors vous aurez besoin d'utiliser la « PARTITION BY » clause 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