2009-10-07 8 views
6

Je suis nouveau à travailler avec des fonctions analytiques.Fonction Oracle Analytic pour la valeur min dans le groupement

 
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 

Je veux le département et l'employé avec un salaire minimum.

Les résultats devraient ressembler à:

 
DEPT EMP SALARY 
---- ----- ------ 
    10 MARY 100000 
    20 BOB 100000 
    30 ALAN 100000 

EDIT: Voici le SQL je (mais bien sûr, cela ne fonctionne pas comme il veut le personnel du groupe par article, ainsi):

 
SELECT dept, 
    emp, 
    MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) 
FROM mytable 
GROUP BY dept 

Répondre

7

Je pense que la fonction Rank() n'est pas la bonne solution, pour deux raisons. Tout d'abord, il est probablement moins efficace qu'une méthode basée sur Min().

La raison en est que la requête doit maintenir une liste ordonnée de tous les salaires par département lors de l'analyse des données, et le classement sera ensuite attribué plus tard en relisant cette liste. Évidemment, en l'absence d'index qui peuvent être utilisés pour cela, vous ne pouvez pas attribuer un rang avant que le dernier élément de données ait été lu, et la maintenance de la liste est coûteuse. Par conséquent, les performances de la fonction Rank() dépendent du nombre total d'éléments à analyser et si le nombre est suffisant pour que le tri déborde sur le disque, les performances s'effondreront.

Ceci est probablement plus efficace:

select dept, 
     emp, 
     salary 
from 
     (
     SELECT dept, 
       emp, 
       salary, 
       Min(salary) Over (Partition By dept) min_salary 
     FROM mytable 
     ) 
where salary = min_salary 
/

Cette méthode exige seulement que la requête à maintenir une valeur unique par département de la valeur minimum rencontrée jusqu'à présent. Si un nouveau minimum est rencontré, la valeur existante est modifiée, sinon la nouvelle valeur est ignorée. Le nombre total d'éléments qui doivent être conservés en mémoire est lié au nombre de départements et non au nombre de lignes analysées.

Il se peut qu'Oracle ait un chemin de code pour reconnaître que le rang n'a pas vraiment besoin d'être calculé dans ce cas, mais je ne parierais pas dessus. La deuxième raison de ne pas aimer Rank() est qu'il répond simplement à la mauvaise question. La question n'est pas "Quels enregistrements ont le salaire qui est le premier classement lorsque les salaires par département sont ordonnés à la hausse", c'est "Quels enregistrements ont le salaire qui est le minimum par département". Cela fait une grande différence pour moi, au moins.

+0

Merci David. Après avoir considéré ses avantages, j'ai refactorisé à votre solution. –

3

Vous pouvez utiliser la syntaxe RANK(). Par exemple, cette requête vous dira où un employé se classe au sein de leur département en ce qui concerne la taille de leur salaire est:

SELECT 
    dept, 
    emp, 
    salary, 
    (RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept 
FROM EMPLOYEES 

Vous pouvez ensuite interroger à partir de ce où 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 
+0

Parfait! Je ne savais pas encore sur RANK(). Merci. –

+0

Je ne savais même pas à propos de RANK() jusqu'à hier! :) –

+1

Je suis downvoting pour les raisons que j'ai décrites dans ma propre réponse: Je pense que c'est probablement inefficace, et je pense que la requête ne correspond pas exactement à la question posée. Je ne dis pas que cela ne donnera pas la bonne réponse, mais simplement que cela n'exprime pas très bien la logique de la question. –

-1
select e2.dept, e2.emp, e2.salary 
from employee e2 
where e2.salary = (select min(e1.salary) from employee e1) 
+1

Cela vous donnera un enregistrement - le minimum pour toute la table. Vous devez regrouper par département dans votre sous-sélection. –

3

Je pense que vous étiez assez proche de votre requête d'origine. Ce qui suit ruisselait et faire correspondre votre cas de test:

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 

Contrairement aux solutions RANK(), celui-ci garantit au plus une ligne par département.Mais cela laisse présager un problème: que se passe-t-il dans un département où il y a deux employés avec le salaire le plus bas? Les solutions RANK() retourneront les deux employés - plus d'une rangée pour le département. Cette réponse en choisira une de façon arbitraire et s'assurera qu'il n'y en a qu'une pour le ministère.

+1

Oui, c'est un bon point sur les enregistrements multiples. La méthode Min() va récupérer tous les doublons ... il sera plus difficile d'obtenir un seul enregistrement pour ceux qui en ont besoin. –

+1

Excellente élaboration - surtout si l'analyse posée est plus concernée par la * valeur * du minimum. Si des * attributs d'identification * du minimum étaient nécessaires, il semblerait souhaitable de préserver les doublons. – Andrew

Questions connexes