2016-11-14 81 views
2

我需要得到員工的最低工資在他們的部門 我做到了使用反連接。我可以通過SQL查詢與窗口功能分組嗎?

 select emp.employee_id,emp.last_name,emp.salary,emp.department_id 
    from employees emp 
    left join employees sml 
    on sml.department_id = emp.department_id and sml.salary < emp.salary 
    where sml.employee_id is null and emp.department_id is not null 

但有人告訴我,它可能使用窗口函數使用一個選擇它做的事。 但是,我無法將其按department_id分組並同時使用它。 這是一個錯誤還是我是愚蠢的?

 SELECT department_id, 
    min(salary) OVER (partition by department_id) as minsalary 
    FROM employees; 
    GROUP BY department_id 

SQL開發人員說00979. 00000 - 「不是一個GROUP BY表達式」

回答

1

如果您在沒有group by的情況下運行第二個查詢(您可能已經嘗試過),則可以從您發佈的額外分號中運行第一個查詢 - 您會看到每個員工都會得到一行,每個員工顯示他們的最低工資部。該最小值是分析min(),因爲它有一個窗口子句。 PARTITION BY相當於GROUP BY,但沒有整個結果集合。

得到相同的結果,最簡單的方式(幾乎)是使用RANK()解析函數來代替,該排名根據您提供的分區和秩序價值,同時允許的關係:

SELECT employee_id, last_name, salary, department_id, 
    RANK() OVER (PARTITION BY department_id ORDER BY salary) AS rnk 
FROM employees 
ORDER BY department_id, rnk; 

EMPLOYEE_ID LAST_NAME      SALARY DEPARTMENT_ID  RNK 
----------- ------------------------- ---------- ------------- ---------- 
     200 Whalen       4400   10   1 
     202 Fay        6000   20   1 
     201 Hartstein      13000   20   2 
     119 Colmenares      2500   30   1 
     118 Himuro       2600   30   2 
     117 Tobias       2800   30   3 
     116 Baida       2900   30   4 
     115 Khoo       3100   30   5 
     114 Raphaely      11000   30   6 
... 
     102 De Haan      17000   90   1 
     101 Kochhar      17000   90   1 
     100 King       24000   90   3 
... 

對於部門20和30你可以看到第1排是最低的薪水。對於90部門,有兩名員工排名第一,因爲他們的薪水最低。

您可以使用它作爲內嵌視圖,並選擇只是那些行排名第1:

SELECT employee_id, last_name, salary, department_id 
FROM (
    SELECT employee_id, last_name, salary, department_id, 
    RANK() OVER (PARTITION BY department_id ORDER BY salary) AS rnk 
    FROM employees 
) 
WHERE rnk = 1 
ORDER BY department_id; 

EMPLOYEE_ID LAST_NAME      SALARY DEPARTMENT_ID 
----------- ------------------------- ---------- ------------- 
     200 Whalen       4400   10 
     202 Fay        6000   20 
     119 Colmenares      2500   30 
     203 Mavris       6500   40 
     132 Olson       2100   50 
     107 Lorentz       4200   60 
     204 Baer       10000   70 
     173 Kumar       6100   80 
     101 Kochhar      17000   90 
     102 De Haan      17000   90 
     113 Popp       6900   100 
     206 Gietz       8300   110 
     178 Grant       7000    

13 rows selected. 

如果您沒有擔心的關係有一個更簡單的選擇,但它ins't適合在這裏。

請注意,這會給您比原始查詢多一行。您正在加入on sml.department_id = emp.department_id。如果部門標識爲null(與員工178相同),則該連接失敗,因爲您無法將null與具有相等性測試的空值進行比較。由於此解決方案沒有加入,所以不適用,並且您在結果中看到該員工。

+0

它工作正常,謝謝。但告訴我,有沒有更簡單的方法?對我來說似乎有點長。我認爲可能有其他方式來分組,更令人愉快和明顯。 –

+0

@ E.Saraf - [FIRST](https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions065.htm#SQLRF00641)是一種替代方案,但每個部門只能獲得一行 - 如此對於部門90你只能得到101或102,而不是兩個。 –

0

你不需要窗口函數在這種情況下,會導致一個簡單的group by將工作太。

並且錯誤是正確的,導致窗口功能不是聚合函數。 而一個窗口函數不能是一個Group by-member。

但是你可以使用「distinct」來代替。

SELECT DISTINCT department_id, 
    min(salary) OVER (partition by department_id) as minsalary 
FROM employees; 

在你的特殊情況下,所有這些都是過大的,當然。但我認爲理解是遊戲的名字。

+0

這隻會給每個部門的最低工資,而不是實際的員工數據 – Matt

+0

@Matt的權利。我需要部門的最低工資,以及員工姓名。 –

1
WITH cte AS (
    SELECT 
     emp.* 
     ,ROW_NUMBER() OVER (PARTITION BY emp.department_id ORDER BY emp.salary) as RowNumber 
    FROM 
     employees emp 
) 

SELECT c.* 
FROM 
    cte c 
WHERE 
    c.RowNumber = 1 

您可以使用ROW_NUMBER()按部門獲得1行最低工資。如果你想在領帶的情況下,所有的行切換到RANK()

否則,您可以用MIN() OVER做到這一點,但是這會給你綁

WITH cte AS (
    SELECT 
     emp.* 
     ,MIN(emp.salary) OVER (PARTITION BY emp.department_id) as DeptMinSalary 
    FROM 
     employees emp 
) 

SELECT c.* 
FROM 
    cte c 
WHERE 
    c.salary = c.DeptMinSalary 

作爲派生表,而不是一個公共表表達式:

SELECT t.* 
FROM 
    (SELECT 
     emp.* 
     ,ROW_NUMBER() OVER (PARTITION BY emp.department_id ORDER BY emp.salary) as RowNumber 
    FROM 
     employees emp) t 
WHERE 
    t.RowNumber = 1 

最後一個想到這個問題,因爲你問「我可以通過SQL查詢進行分組和窗口函數嗎?」 Alex涵蓋了PARTITION BY就像是窗口函數中的子分組。但是,使用帶有窗口函數的GROUP BY分組意味着GROUP BY結果集將在PRIOR之前評估爲正在評估的窗口函數。

+0

Dorry,我沒有弄明白,我被告知可以用一個SELECT語句來做。是這樣嗎? –

+0

這仍然被認爲是1選擇,因爲它是一個通用表格表達式。基本上你可以將cte移動到派生表中,如果你想的話。但答案是否定的,只有一個選擇窗口函數是不可能的,因爲where語句是在窗函數之前求值的。但2選擇仍被視爲1查詢 – Matt

+0

從僱員選擇*是好的;但是'select *, from employees' [在Oracle中不允許](http://stackoverflow.com/q/1751856/266304) - 除非你在表名/別名前面加了'*'。所以你最新的編輯是好的(雖然已經+1了) –