5

Oracle 11g では、テーブルをクエリして、特定のグループの最大値と最小値を持つ行から情報を引き出すことができる必要があります。たとえば、EMP テーブルを使用して、各部門で最も給与が高い人の名前と最も給与が低い人の名前を見つけたいとします。

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

(給与が最高または最低のスタッフが 2 人以上いる場合は、常に最初のスタッフをアルファベット順に返します)。

以前の投稿では、最大値と最小値の両方ではなく、最大値のみの値を取得する方法について説明しました。

現時点では、上記のリンクに基づいて、次のクエリを適用するための厄介なソリューションがありますが、パフォーマンスは重要です。優れたソリューションには、分析関数と、場合によっては複数の行を単一の行に結合するためのピボットも必要になると予測しています。

どんな助けでも大歓迎です!リチャード

4

2 に答える 2

5

これは、分析関数を使用して簡単に解決できます。ご覧のとおり、DEPT 20 には 2 人の従業員が最大の給与を獲得しています。これは重要な詳細です。この種の問題に対する一般的な解決策には、その情報が欠けているものがあります。

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>

おっと、結果の形式に関する重要な詳細を見逃していました。最大給与を獲得している従業員が 2 人いるため、私のデータは要求された出力に適合しません。したがって、このクエリは少し厄介だと思いますが、必要なレイアウトが得られます。従業員名の 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>

私はこの解決策が好きではありません。ほとんどのデータセットにはそのような衝突が含まれており、それらを認める必要があります。Procrustian のレポート レイアウトに適合させるために、いくつかの無関係な基準に基づいて結果をフィルター処理することは、誤解を招く可能性があります。データセット全体を反映したレポート レイアウトを希望します。最終的には、クエリが提供するビジネスの目的によって異なります。そしてもちろん、顧客は常に正しい 8-)

于 2011-05-04T09:26:52.233 に答える
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
);

アップデート

「サラリーが最高または最低のスタッフが2人以上いる場合は、常に最初のスタッフをアルファベット順に返したい」という他の要件を達成するために、以下のようにクエリを少し調整することができます(ここでの改善):

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);
于 2011-05-04T09:08:44.490 に答える