oracle 我可以在带有窗口函数的SQL查询中使用group by吗?

tgabmvqs  于 2022-11-03  发布在  Oracle
关注(0)|答案(5)|浏览(238)

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"

avwztpqn

avwztpqn1#

首先要记住的是窗口函数(如OVER()子句)作用于查询的结果,即:服务器首先执行查询,然后才应用您定义的窗口化函数(当然,这对实际发生的事情过于简单,但足以说明我的观点)。
这意味着您实际上可以在同一个查询中使用窗口函数和group by子句,但是您需要使用windowed function聚合来封装group by聚合,如下所示:

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

然而,我同意这里不是使用窗口函数的好地方,Matt的建议--我赞成,完全公开--在这里是最好的(ROW_NUMBER()CTEsubquery中,然后在主SELECT中只选择所需的行)。

zmeyuzjn

zmeyuzjn2#

如果运行第二个查询时不使用group by(您可能已经尝试过了,因为您发布的内容中有一个额外的分号),您将看到每个雇员都有一行,每行显示其所在部门的最低工资。最低工资是分析min(),因为它有一个window子句。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,因为他们具有相同的最低薪金。
您可以将其用作内联视图,并仅选择排名为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.

如果你不必担心领带,还有一个更简单的选择,但它不适合在这里。
请注意,这比原始查询多了一行。您要联接on sml.department_id = emp.department_id。如果部门ID为空(如雇员178),则该联接将失败,因为您无法使用相等性测试比较空值与空值。由于此解决方案没有联接,因此不适用,并且您将在结果中看到该雇员。

nvbavucw

nvbavucw3#

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查询中使用group by吗?”Alex解释了PARTITION BY就像是窗口函数中的子分组。但是,将GROUP BY分组与窗口函数一起使用意味着GROUP BY结果集将在窗口函数被求值之前求值。

zf9nrax1

zf9nrax14#

在这种情况下,你不需要窗口函数,因为一个简单的group by也可以工作。
错误是正确的,因为窗口函数不是聚合函数。并且窗口函数不能是Group by- member。
但你可以用“不同的”来代替。

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

当然,在你的特殊情况下,这一切都是多余的。但我认为理解是游戏的名字。

fcipmucu

fcipmucu5#

由于窗口函数不是聚集函数,因此您需要在group by下放置非聚集标签,如department_id、salary。根据您的问题,不建议使用窗口函数。

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

相关问题