在Oracle 11g中,我们需要能够查询表以从某个组中具有最高和最低值的行中提取信息。例如,使用EMP表,我们希望查找每个部门中工资最高的人员的姓名和工资最低的人员的姓名
DEPTNO MAX_SAL MAX_EARNER MIN_SAL MIN_EARNER
-------------------------------------------------------
10 5000 KING 1300 MILLER
20 3000 FORD 2975 JONES
etc
(如果有两个或两个以上员工的工资最高或最低,我们希望始终按字母顺序返回第一个)。
上一篇文章讨论了如何获得最大值而不是最大值和最小值。
基于上面的链接,我们目前有一个不太整洁的解决方案,然后应用后续查询,但性能对我们来说很重要。我预测一个好的解决方案还需要分析函数和可能的枢轴来将多行合并为单行。
任何帮助都非常感谢!理查德。
这很容易用解析函数求解。如您所见,在DEPT 20中有两名员工的工资最高;这是一个重要的细节,因为这类问题的一些常见解决方案忽略了这个信息。
SQL> select ename
2 , deptno
3 , sal
4 from (
5 select ename
6 , deptno
7 , sal
8 , max (sal) over (partition by deptno) max_sal
9 , min (sal) over (partition by deptno) min_sal
10 from emp
11 )
12 where sal = max_sal
13 or sal = min_sal
14 order by deptno, sal
15 /
ENAME DEPTNO SAL
---------- ---------- ----------
KISHORE 10 1300
SCHNEIDER 10 5000
CLARKE 20 800
RIGBY 20 3000
GASPAROTTO 20 3000
HALL 30 950
LIRA 30 3750
TRICHLER 50 3500
FEUERSTEIN 50 4500
9 rows selected.
SQL>
哎呀,我错过了一个关于结果格式的重要细节。我的数据不符合要求的输出,因为有两名员工的工资最高。所以这个查询,我承认有点尴尬,给了我们所需的布局。对员工姓名的MIN()返回字母顺序:
SQL> select
2 deptno
3 , max (case when sal = min_sal then min_sal else null end ) as min_sal
4 , min (case when sal = min_sal then ename else null end ) as min_name
5 , max (case when sal = max_sal then max_sal else null end ) as max_sal
6 , min (case when sal = max_sal then ename else null end ) as max_name
7 from (
8 select ename
9 , deptno
10 , sal
11 , max (sal) over (partition by deptno) max_sal
12 , min (sal) over (partition by deptno) min_sal
13 from emp
14 )
15 where sal = max_sal
16 or sal = min_sal
17 group by deptno
18 order by deptno
19 /
DEPTNO MIN_SAL MIN_NAME MAX_SAL MAX_NAME
---------- ---------- ---------- ---------- ----------
10 1300 KISHORE 5000 SCHNEIDER
20 800 CLARKE 3000 GASPAROTTO
30 950 HALL 3750 LIRA
50 3500 TRICHLER 4500 FEUERSTEIN
SQL>
我不喜欢这个解决方案。大多数数据集都会包含这样的冲突,我们需要承认它们。基于一些不相关的标准来过滤结果以适应Procrustean报告布局是误导的。我更喜欢反映整个数据集的报告布局。最终,这取决于查询服务的业务目的。当然,顾客永远是对的
您可以使用下面的查询
SELECT
dept,
max_sal,
(SELECT emp_name FROM emp WHERE salary = max_sal AND rownum =1) max_earner,
min_sal,
(SELECT emp_name FROM emp WHERE salary = min_sal AND rownum =1) min_earner
FROM
(SELECT
dept,
MAX(salary) max_sal,
MIN(salary) min_sal
FROM emp
GROUP BY dept);
假设您的表是这样的:
CREATE TABLE emp
(
dept NUMBER,
emp_name VARCHAR2(20 BYTE),
salary NUMBER
);
为了实现您的其他要求"如果有两个或两个以上的员工与最高或最低的工资,我们希望始终返回第一个按字母顺序",您可以调整查询一点如下(我相信这里有改进的余地):
SELECT
dept,
max_sal,
(select emp_name from (SELECT * FROM emp order by emp_name asc) WHERE salary = max_sal AND rownum =1) max_earner,
min_sal,
(select emp_name from (SELECT * FROM emp order by emp_name asc) WHERE salary = min_sal AND rownum =1) min_earner
FROM
(SELECT
dept,
MAX(salary) max_sal,
MIN(salary) min_sal
FROM emp
GROUP BY dept);