Question

Considérez l'Oracle emp tableau.J'aimerais que les employés ayant les salaires les plus élevés department = 20 et job = clerk.Supposons également qu’il n’y ait pas de colonne « empno » et que la clé primaire implique un certain nombre de colonnes.Vous pouvez le faire avec :

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 dupliquer le test deptno = 20 et job = 'CLERK', ce que j'aimerais éviter.Existe-t-il une façon plus élégante d'écrire ceci, peut-être en utilisant un group by?BTW, si cela compte, j'utilise Oracle.

Était-ce utile?

La solution

Ce qui suit est légèrement sur-conçu, mais constitue un bon modèle SQL pour les requêtes "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
  )

Notez également que cela fonctionnera dans Oracle et Postgress (je pense) mais pas dans MS SQL.Pour quelque chose de similaire dans MS SQL, voir la question Requête SQL pour obtenir le dernier prix

Autres conseils

Si j'étais certain de la base de données ciblée, j'opterais pour la solution de Mark Nold, mais si jamais vous voulez du SQL* indépendant du dialecte, 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 pense que cela devrait fonctionner partout, mais je n'ai pas les environnements pour le tester.

Dans Oracle, je le ferais avec une fonction analytique, vous n'interrogeriez donc la table emp qu'une seule 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

C'est plus simple, plus facile à lire et plus efficace.

Si vous souhaitez le modifier pour répertorier ces informations pour tous les départements, vous devrez alors utiliser la clause « PARTITION BY » dans 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

C'est super!Je ne savais pas que l'on pouvait comparer (x, y, z) avec le résultat d'une instruction SELECT.Cela fonctionne très bien avec Oracle.

En guise de remarque pour les autres lecteurs, il manque un "=" après "(deptno,job,sal)" dans la requête ci-dessus.Peut-être que le formateur Stack Overflow l'a mangé (?).

Encore une fois merci Marc.

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

Par exemple...Sélectionnez Nom, numéro dans Cust Where Cust In (SELECT CUST_ID FROM BIG_TABLE) et entré> Sysdate -1 serait lent.

Mais sélectionnez Nom, numéro dans Cust cOù existe (sélectionnez Cust_id dans big_table OÙ cust_id=c.cust_id ) Et entré> sysdate -1 serait très rapide avec une indexation appropriée.Vous pouvez également l'utiliser avec plusieurs paramètres.

Il existe de nombreuses solutions.Vous pouvez également conserver la présentation de votre requête d'origine en ajoutant simplement des alias de table et en joignant les noms de colonnes, vous n'aurez toujours qu'une seule fois DEPTNO = 20 et JOB = 'CLERK' dans la requête.

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
    )

On pourrait également noter que le mot clé « ALL » peut être utilisé pour ce type de requêtes ce qui permettrait 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top