2009-10-07 17 views
6

分析関数を使用するのが初めてです。グループ化の最小値に対するOracle Analytic関数

 
DEPT EMP SALARY 
---- ----- ------ 
    10 MARY 100000 
    10 JOHN 200000 
    10 SCOTT 300000 
    20 BOB 100000 
    20 BETTY 200000 
    30 ALAN 100000 
    30 TOM 200000 
    30 JEFF 300000 

最低給与の部門と従業員が必要です。

 
DEPT EMP SALARY 
---- ----- ------ 
    10 MARY 100000 
    20 BOB 100000 
    30 ALAN 100000 

EDIT:

結果は次のようになります。ここで私が持っているSQLは、(それは同様GROUP BY句でスタッフを望んでいるとしてではなく、もちろん、それは動作しません)です:

 
SELECT dept, 
    emp, 
    MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary) 
FROM mytable 
GROUP BY dept 

答えて

7

を私は、Rank()関数は2つの理由からこれを行う方法ではないと思います。

まず、Min()ベースのメソッドよりも効率が悪い可能性があります。

この理由は、データをスキャンするときに部門ごとのすべての給与の順序付きリストをクエリで保持しなければならないため、後でこのリストを読み直すことによってランクが割り当てられるからです。明らかに、このために利用できるインデックスがない場合、最後のデータ項目が読み込まれ、リストのメンテナンスが高価になるまでランクを割り当てることはできません。

Rank()関数のパフォーマンスは、スキャンする要素の総数に依存します。ソートがディスクに流出するのに十分な数であれば、パフォーマンスは低下します。

これはおそらく、より効率的である:

select dept, 
     emp, 
     salary 
from 
     (
     SELECT dept, 
       emp, 
       salary, 
       Min(salary) Over (Partition By dept) min_salary 
     FROM mytable 
     ) 
where salary = min_salary 
/

このメソッドは、唯一のクエリがこれまでに遭遇した最小値の部門ごとに単一の値を維持することが必要です。新しい最小値に達した場合は、既存の値が変更され、そうでない場合は新しい値が破棄されます。メモリに保持されなければならない要素の総数は、スキャンされた行の数ではなく、部署の数に関係します。

Oracleには、ランクを実際に計算する必要はないことを認識するためのコードパスがありますが、私はそれに賭けません。

Rank()を嫌う第2の理由は、間違った質問に答えることだけです。問題は「部門ごとの給与が昇順になったときにどのような記録が最初の給料であるか」ではなく、「どの部門が給料が部門ごとに最低であるか」である。それは少なくとも、私には大きな違いをもたらします。

+0

Davidありがとう。そのメリットを考慮して、私はあなたのソリューションにリファクタリングしました。 –

3

RANK()構文を使用できます。従業員が自分の給料がどのように大規模に関しては、部門内にランクどこたとえば、このクエリはあなたを教えてくれます:

SELECT 
    dept, 
    emp, 
    salary, 
    (RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept 
FROM EMPLOYEES 

をあなたは、このところsalary_rank_within_dept = 1からの照会ができます

SELECT * FROM 
    (
    SELECT 
     dept, 
     emp, 
     salary, 
     (RANK() OVER (PARTITION BY dept ORDER BY salary)) salary_rank_within_dept 
    FROM EMPLOYEES 
) 
WHERE salary_rank_within_dept = 1 
+0

完璧!私はまだRANK()について知らなかった。ありがとう。 –

+0

私は昨日までRANK()について知りませんでした! :) –

+1

私は私自身の答えで概説した理由でこれをdownvotingしている:私はそれがおそらく非効率であると思う、と私は質問が正確な質問に一致していないと思う。私は正しい答えを出さないと言っているわけではありません。ちょうど質問の論理をうまく表現していないということだけです。 –

-1
select e2.dept, e2.emp, e2.salary 
from employee e2 
where e2.salary = (select min(e1.salary) from employee e1) 
+1

これはあなたに1つのレコードを与えます - テーブル全体の最小値。サブセレクトの部署ごとにグループ化する必要があります。 –

3

あなたは元のクエリに近いと思います。以下は、実行しますと、あなたのテストケースと一致しない:RANK()ソリューションとは対照的に

SELECT dept, 
    MIN(emp) KEEP(DENSE_RANK FIRST ORDER BY salary, ROWID) AS emp, 
    MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary, ROWID) AS salary 
FROM mytable 
GROUP BY dept 

を、この1は、部門ごとに最大で1つの行で保証しています。しかし、それは問題を示唆しています。最低給与に2人の従業員がいる部門ではどうなりますか? RANK()ソリューションは、両方の従業員(部門の複数の行)を返します。この回答は任意に選択され、部門の唯一のものがあることを確認します。

+1

ええ、それは複数のレコードの良い点です。 Min()メソッドは、すべての重複を取得します。必要な場合は、単一のレコードを戻すのが難しくなります。 –

+1

優れた精緻化 - 特に分析が行われている場合は、最小値の* value *にもっと関係しています。最小の属性*を特定する必要がある場合は、重複を保存することが望ましいと思われる。 – Andrew

関連する問題