2008-09-25 6 views
7

Comment pourrais-je obtenir N résultats pour plusieurs groupes dans une requête oracle.Obtenir les meilleurs résultats pour chaque groupe (dans Oracle)

Par exemple, étant donné le tableau suivant:

|--------+------------+------------| 
| emp_id | name  | occupation | 
|--------+------------+------------| 
|  1 | John Smith | Accountant | 
|  2 | Jane Doe | Engineer | 
|  3 | Jack Black | Funnyman | 
|--------+------------+------------| 

Il y a beaucoup plus de lignes avec plus professions. Je voudrais obtenir trois employés (disons) de chaque profession.

Existe-t-il un moyen de le faire sans utiliser de sous-requête?

Répondre

11

Cela produit ce que vous voulez, et n'utilise pas de fonctionnalités SQL spécifiques au fournisseur comme TOP N ou RANK().

SELECT e.name, e.occupation 
FROM emp AS e 
    LEFT OUTER JOIN emp AS e2 
    ON (e.occupation = e2.occupation AND e.emp_id <= e2.emp_id) 
GROUP BY e.emp_id 
HAVING COUNT(*) <= 3 
ORDER BY e.occupation; 

Dans cet exemple, il donne les trois employés avec les valeurs les plus basses d'emp_id par occupation. Vous pouvez changer l'attribut utilisé dans la comparaison des inégalités, pour lui donner le nom des meilleurs employés, ou peu importe.

+3

Je pense que cela échouera sur la clause GROUP BY. Il n'y a pas de fonctions agrégées dans votre liste de sélection. – jop

+1

J'ai testé cette requête avant de la poster. SQL ne nécessite pas de fonctions d'agrégation pour utiliser GROUP BY. –

2

Je ne suis pas sûr que ce soit très efficace, mais peut-être un point de départ?

select * 
from people p1 
    join people p2 
     on p1.occupation = p2.occupation 
    join people p3 
     on p1.occupation = p3.occupation 
     and p2.occupation = p3.occupation 
where p1.emp_id != p2.emp_id 
    and p1.emp_id != p3.emp_id 

Ceci devrait vous donner des lignes qui contiennent 3 employés distincts tous dans la même occupation. Malheureusement, il vous donnera toutes les combinaisons de ceux-ci.

Quelqu'un peut-il réduire cela s'il vous plaît?

28

Je n'ai pas une instance oracle pratique en ce moment, donc je ne l'ai pas testé ceci:

select * 
from (select emp_id, name, occupation, 
     rank() over (partition by occupation order by emp_id) rank 
     from employee) 
where rank <= 3 

Voici un lien sur la façon dont fonctionne le rang: http://www.psoug.org/reference/rank.html

+2

didnt il précise sans sous-requête ...? – AviD

+1

Oui, mais il peut bien vouloir dire "sans utiliser une sous-requête qui sélectionne de nouveau la même table". Cette solution utilise une sous-requête mais n'accède à la table qu'une seule fois. –

2

testé cela dans SQL Server (et il utilise sous-requête)

select emp_id, name, occupation 
from employees t1 
where emp_id IN (select top 3 emp_id from employees t2 where t2.occupation = t1.occupation) 

juste faire une commande par la sous-requête pour répondre à vos besoins

2

Ajouter ROWNUM au rang:

select * from 
     (select emp_id, name, occupation,rank() over (partition by occupation order by emp_id,RowNum) rank 
         from employee) 
     where rank <= 3 
Questions connexes