2009-10-07 14 views
6

Jestem nowy w pracy z funkcjami analitycznymi.Funkcja analityczna Oracle dla minimalnej wartości w grupie

 
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 

Chcę działu i pracownika z minimalnym wynagrodzeniem.

Wyniki powinny wyglądać następująco:

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

EDIT: Tutaj jest SQL mam (ale oczywiście to nie działa jak chce personelu w grupie przez klauzuli, jak również):

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

Odpowiedz

7

Myślę, że funkcja Rank() nie jest drogą do tego, z dwóch powodów.

Po pierwsze jest prawdopodobnie mniej wydajna niż metoda oparta na Min().

Powodem jest to, że zapytanie musi zachować uporządkowaną listę wszystkich pensji na dział, ponieważ skanuje dane, a ranga zostanie przypisana później poprzez ponowne przeczytanie tej listy. Oczywiście w przypadku braku indeksów, które można wykorzystać do tego celu, nie można przypisać rankingu, dopóki ostatni element danych nie zostanie odczytany, a konserwacja listy jest droga.

Tak więc wydajność funkcji Rank() zależy od łącznej liczby skanowanych elementów, a jeśli liczba jest wystarczająca, aby sortowanie zostało rozlane na dysk, wydajność zostanie zwinięta.

Jest to prawdopodobnie bardziej efektywne:

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

Ta metoda wymaga jedynie, że zapytanie utrzymać pojedynczą wartość na wydziale minimalnej wartości spotykanych do tej pory. Jeśli napotkane zostanie nowe minimum, wówczas zmieni się istniejąca wartość, w przeciwnym razie nowa wartość zostanie odrzucona. Łączna liczba elementów, które muszą być przechowywane w pamięci, zależy od liczby działów, a nie od liczby przeskanowanych wierszy.

Może się zdarzyć, że Oracle ma ścieżkę do kodu, aby rozpoznać, że Rank nie musi być w tym przypadku wyliczany, ale nie postawiłbym na to.

Drugą przyczyną niechęci do Rang() jest odpowiedź na niewłaściwe pytanie. Pytanie nie brzmi: "Które rekordy mają pensję, która jest pierwszym rankingiem, kiedy pensje na wydział są sortowane rosnąco", jest to "Które rekordy mają wynagrodzenie, które jest minimalne dla każdego działu". Co najmniej ma dla mnie dużą różnicę.

+0

Dziękuję David. Po rozważeniu jego zalet, refakturowałem do twojego rozwiązania. –

3

Można użyć składni RANK(). Na przykład, zapytanie to powie Ci, gdzie pracownik zajmuje w ich działu w odniesieniu do tego, jak duża ich wynagrodzenie jest:

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

Można wtedy zapytania od tego gdzie 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

Idealny! Nie wiedziałem jeszcze o RANK(). Dzięki. –

+0

Nawet nie wiedziałem o RANK() aż do wczoraj! :) –

+1

Obalam to z powodów, które opisałem w mojej własnej odpowiedzi: Myślę, że to prawdopodobnie nieefektywne i myślę, że zapytanie nie pasuje dokładnie do zadawanego pytania. Nie twierdzę, że nie da to prawidłowej odpowiedzi, tylko że nie wyraża ona logiki pytania bardzo dobrze. –

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

To da ci jeden rekord - minimum dla całego stołu. Musisz pogrupować według działu w swojej podselekcji. –

3

Wydaje mi się, że jesteś blisko swojego pierwotnego zapytania. Poniższa będzie biegać i robić dopasować przypadek testowy:

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 

w przeciwieństwie do rangi() rozwiązań, ten gwarantuje co najwyżej jednym rzędzie na wydziale.Ale to wskazuje na problem: co dzieje się w dziale, w którym zatrudnionych jest dwóch pracowników o najniższej pensji? Rozwiązania RANK() zwrócą obu pracowników - więcej niż jeden wiersz dla działu. Ta odpowiedź wybierze jednego arbitra i upewni się, że jest tylko jeden dla departamentu.

+1

Tak, to dobry punkt w wielu rekordach. Metoda() Min() będzie odzyskać wszystkie duplikaty ... być trudniejsze, aby uzyskać jeden rekord z powrotem dla tych, jeśli jeden był potrzebny. –

+1

Doskonałe opracowanie - szczególnie, jeśli analiza jest bardziej związana z * wartością * minimum. Jeśli potrzebne są * identyfikujące atrybuty * minimum, zachowywanie duplikatów wydaje się pożądane. – Andrew