2009-10-07 15 views
6

我是使用分析函數工作的新手。Oracle分析函數用於分組最小值

 
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 

我希望部門和員工有最低工資。

結果應該是這樣的:

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

編輯:這裏是SQL我已經(當然,這是行不通的,因爲它的條款要在組中的工作人員也一樣):

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

回答

7

我認爲Rank()函數並不適合這樣做,原因有兩個。首先,它可能比基於Min()的方法效率低。

這樣做的原因是,查詢有,因爲它掃描的數據,以保持每個部門所有薪水的有序列表,而排名隨後將​​通過重新閱讀這個名單以後分配。很明顯,如果沒有可用於此的索引,則直到讀取最後一個數據項才能分配排名,並且維護該列表的代價很高。

所以秩()函數的性能依賴於要被掃描的元件的總數量,以及如果所述數目是足夠的排序溢出到磁盤然後性能會崩潰。

這可能是更有效的:

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

這種方法只需要查詢保持每到目前爲止遇到的最小值部門的單一值。如果遇到新的最小值,則修改現有值,否則丟棄新值。內存中要保存的元素總數與部門數量有關,而不是掃描的行數。

這可能是因爲Oracle有一個代碼路徑認識到,排名並不真正需要在這種情況下計算的,但我不會賭。

的第二個原因不喜歡RANK()是,它只是回答錯誤的問題。問題不在於「哪些記錄的薪水是每個部門薪水上漲時排在第一位的薪水」,而是「哪些記錄的薪水是每個部門的最低薪金」。至少,這對我至關重要。

+0

謝謝大衛。考慮到它的好處後,我重構了您的解決方案。 – 2009-10-09 18:21:42

3

您可以使用RANK()語法。例如,這個查詢將告訴你在哪裏僱員他們對於部門內行列,他們的工資有多大:

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

然後你可以從這個查詢,其中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

完美!我還不知道RANK()。謝謝。 – 2009-10-07 18:14:20

+0

我甚至不知道RANK(),直到昨天! :) – 2009-10-07 18:15:27

+1

由於我在自己的回答中列出的原因,我對此進行了下調:我認爲這可能效率低下,並且我認爲查詢與要問的確切問題不太匹配。我並不是說它不會給出正確的答案,只是它沒有很好地表達這個問題的邏輯。 – 2009-10-08 07:11:08

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

這會給你一個記錄 - 整個表的最小值。你需要在你的子選擇中由部門分組。 – 2009-10-07 18:12:28

3

我覺得你很接近你的原始查詢。下面將運行並做符合您的測試用例:

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 

與此相反的RANK()解決方案,在每個部門最多一個行這一個保證。但是這暗示了一個問題:在有兩名僱員薪水最低的部門會發生什麼? RANK()解決方案將返回兩個員工 - 部門不止一行。這個答案會任意挑選一個,並確保只有一個部門。

+1

是的,這是多個記錄上的一個優點。 Min()方法將檢索所有重複項......如果需要的話,可以更復雜地獲取單個記錄。 – 2009-10-09 12:02:57

+1

優秀的闡述 - 尤其是如果提出的分析更關注最小值的*值*。如果需要識別最小屬性*,則保留重複項似乎是可取的。 – Andrew 2011-10-17 15:15:43