I need to getemployees with smallest salary in their departmentsI did it using anti join.
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
But I've been told that it's possible to do it using window functionusing one select. However I can't group it by department_id and use it at the same time. Is that a bug or me being stupid?
SELECT department_id,
min(salary) OVER (partition by department_id) as minsalary
FROM employees;
GROUP BY department_id
SQL Developer says 00979. 00000 - "not a GROUP BY expression"
5条答案
按热度按时间avwztpqn1#
首先要记住的是窗口函数(如
OVER()
子句)作用于查询的结果,即:服务器首先执行查询,然后才应用您定义的窗口化函数(当然,这对实际发生的事情过于简单,但足以说明我的观点)。这意味着您实际上可以在同一个查询中使用窗口函数和group by子句,但是您需要使用
windowed function
聚合来封装group by
聚合,如下所示:然而,我同意这里不是使用窗口函数的好地方,Matt的建议--我赞成,完全公开--在这里是最好的(
ROW_NUMBER()
在CTE
或subquery
中,然后在主SELECT
中只选择所需的行)。zmeyuzjn2#
如果运行第二个查询时不使用
group by
(您可能已经尝试过了,因为您发布的内容中有一个额外的分号),您将看到每个雇员都有一行,每行显示其所在部门的最低工资。最低工资是分析min()
,因为它有一个window子句。PARTITION BY
等效于GROUP BY
。但不对整个结果集进行聚合。要得到几乎相同的结果,最简单的方法是使用
RANK()
分析函数,它根据您提供的分区和顺序对值进行排序,同时允许出现并列:对于部门20和30,您可以看到排名第1的行是最低薪金。对于部门90,有两名员工排名第1,因为他们具有相同的最低薪金。
您可以将其用作内联视图,并仅选择排名为1的行:
如果你不必担心领带,还有一个更简单的选择,但它不适合在这里。
请注意,这比原始查询多了一行。您要联接
on sml.department_id = emp.department_id
。如果部门ID为空(如雇员178),则该联接将失败,因为您无法使用相等性测试比较空值与空值。由于此解决方案没有联接,因此不适用,并且您将在结果中看到该雇员。nvbavucw3#
您可以使用
ROW_NUMBER()
来取得1列按部门列出的最低工资,如上所述。如果您想要所有列都有相同的工资,请将其切换为RANK()
否则你可以用
MIN() OVER
来做,但这会给你带来领带作为派生表而不是公用表表达式:
关于这个主题的最后一个想法,因为您问“我可以在带窗口函数的SQL查询中使用group by吗?”Alex解释了
PARTITION BY
就像是窗口函数中的子分组。但是,将GROUP BY
分组与窗口函数一起使用意味着GROUP BY
结果集将在窗口函数被求值之前求值。zf9nrax14#
在这种情况下,你不需要窗口函数,因为一个简单的
group by
也可以工作。错误是正确的,因为窗口函数不是聚合函数。并且窗口函数不能是Group by- member。
但你可以用“不同的”来代替。
当然,在你的特殊情况下,这一切都是多余的。但我认为理解是游戏的名字。
fcipmucu5#
由于窗口函数不是聚集函数,因此您需要在group by下放置非聚集标签,如department_id、salary。根据您的问题,不建议使用窗口函数。