获取与Oracle中的max和min行相关的值

时间:2011-05-04 08:35:42

标签: sql oracle oracle11g analytic-functions

在Oracle 11g中,我们需要能够查询表以从特定组中具有最高和最低值的行中提取信息。例如,使用EMP表我们想找到薪水最高的人的姓名和每个部门薪水最低的人的姓名

DEPTNO   MAX_SAL    MAX_EARNER    MIN_SAL    MIN_EARNER
-------------------------------------------------------
10       5000       KING          1300       MILLER
20       3000       FORD          2975       JONES
etc

(如果有两个或更多的工作人员具有最高或最低的salaray,我们希望始终按字母顺序返回第一个。)

A Previous Post讨论了如何获得最大值但不是最大值和最小值的值。

我们目前有一个不整洁的解决方案,基于上面的链接然后应用子查询,但性能对我们很重要。我预测一个好的解决方案还需要分析函数,并可能需要一个数据透视表来将多行合并为单行。

任何帮助非常感谢! 理查德

2 个答案:

答案 0 :(得分:5)

这很容易通过分析函数解决。如您所见,有两名员工在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报告布局是误导性的。我更喜欢反映整个数据集的报表布局。最终,它取决于查询所服务的业务目的。当然,客户总是正确的8 - )

答案 1 :(得分:1)

您可以使用以下查询

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
);

<强>更新

为了实现您的其他要求“如果有两个或更多具有最高或最低salaray的员工,我们希望始终按字母顺序返回第一个”,您可以稍微调整一下查询如下(我确信这里有改进的余地):

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);