2016-11-14 53 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了) –