SQL 返回非分组列,你正在执行 GROUP BY
查询,并希望通过 SELECT
列表返回一些列,但这些列却不会出现在 GROUP BY
子句里。这通常无法办到,因为不能保证这些列在每个分组里都有唯一的值。
SQL 返回非分组列 问题描述
你正在执行 GROUP BY
查询,并希望通过 SELECT
列表返回一些列,但这些列却不会出现在 GROUP BY
子句里。这通常无法办到,因为不能保证这些列在每个分组里都有唯一的值。
假设你希望找出每个部门工资最高和最低的员工,同时也希望找出每个职位对应的工资最高和最低的员工。你想查看每个员工的名字、部门、职位以及工资。你希望返回如下所示的结果集。
不幸的是,如果把上述所有列都放入 SELECT
子句的话,将会破坏分组操作。考虑如下的例子。员工 KING 的工资最高,你想用下列查询验证这一点。
以上查询将返回 EMP
表的全部 14 行数据,而不只是 KING 及其工资。之所以会这样,正是因为那个分组操作的存在:它会针对每个 ENAME
调用 MAX(SAL)
函数。因此,上述 SQL 语句看起来好像能够“找出工资最高的员工”,但实际的执行结果却是“找出了 EMP
表里每个 ENAME
对应的最高工资”。因此,本实例将介绍一种能够查询到 ENAME
却不必把 ENAME
放入 GROUP BY
子句的方法。
SQL 返回非分组列 解决方案
使用内嵌视图找出每个 DEPTNO
和 JOB
对应的最高和最低的工资。然后,筛选出工资等于这些值的员工。
DB2、Oracle 和 SQL Server
使用窗口函数 MAX OVER
和 MIN OVER
找出每个 DEPTNO
和 JOB
对应的最高和最低的工资。然后,筛选出工资与之匹配的行。
PostgreSQL 和 MySQL
使用标量子查询找出每个 DEPTNO
和 JOB
对应的最高和最低的工资。然后,只保留与之匹配的员工。
SQL 返回非分组列 扩展知识
DB2、Oracle 和 SQL Server
首先使用窗口函数 MAX OVER
和 MIN OVER
找出每个 DEPTNO
和 JOB
对应的最高和最低的工资。
现在,每个人的工资都可以和当前 DEPTNO
和 JOB
对应的最高和最低的工资进行比较了。需要注意的是,上述窗口函数背后的分组操作(上述 SELECT
子句里的那 4 列)并不会影响 MIN OVER
和 MAX OVER
函数的返回值。这充分展现了窗口函数的优雅之处:聚合运算是基于指定的“分组”或分区展开的,并且会为每个分组返回多行数据。最后只要把上述窗口函数调用放入一个内嵌视图,并且只保留那些与窗口函数返回值相匹配的行即可。在最终的结果集中,我们将使用 CASE
表达式显示每个员工的“状态”。
PostgreSQL 和 MySQL
首先使用标量子查询找出每个 DEPTNO
和 JOB
对应的最高和最低的工资。
现在,每个 DEPTNO
和 JOB
对应的最高和最低的工资可以和 EMP
表里其他的工资比较了。最后,把上述标量子查询放入到一个内嵌视图里,并且只保留那些工资与之相匹配的员工即可。在最终的结果集中,我们将使用 CASE
表达式显示每个员工的“状态”。