2013-11-22 30 views
10

同じ結果を持つような2つのクエリに出くわしました。PARTITION BY(OracleでKEEPを使用している場合と使用していない場合)

これらの2つのクエリの間に差がある場合、私は疑問に思って:

SELECT empno, 
    deptno, 
    sal, 
    MIN(sal) OVER (PARTITION BY deptno) "Lowest", 
    MAX(sal) OVER (PARTITION BY deptno) "Highest" 
FROM empl 

SELECT empno, 
    deptno, 
    sal, 
    MIN(sal) KEEP (DENSE_RANK FIRST ORDER BY sal) OVER (PARTITION BY deptno) "Lowest", 
    MAX(sal) KEEP (DENSE_RANK LAST ORDER BY sal) OVER (PARTITION BY deptno) "Highest" 
FROM empl 

最初のバージョンは、1つのいくつかの種類の特殊な場合、おそらくいくつかのパフォーマンスの最適化もあり、より論理が、2番目です。

答えて

19
MIN(sal) KEEP (DENSE_RANK FIRST ORDER BY sal) OVER (PARTITION BY deptno) 

ステートメントは(約)右から左へ順に考慮することができる。

  • OVER (PARTITION BY deptno)deptnoの別個のグループに行を分割する手段と
  • ORDER BY salは、各パーティションについて、行をsal(暗黙的にはASCの終了順序を使用)で順序付けることを意味します。
  • KEEP (DENSE_RANK FIRSTは、各パーティションの順序付き行に「オリンピック」ランクを与え(順序付け列の値が同じ行には同じランクが与えられます)、最初にランク付けされていない行をすべて破棄することを意味します。最終的に各パーティションの残りの行について
  • MIN(sal)の場合は、最低給与を返します。この場合MINDENSE_RANK FIRST

はどちらも同じことを行いますので、sal列に動作しているとKEEP (DENSE_RANK FIRST ORDER BY sal)は冗長です。

あなたが最小のために別の列を使用する場合は、あなたが見ることができる効果:

SQL Fiddle

Oracleの11グラムR2スキーマのセットアップ

CREATE TABLE test (name, sal, deptno) AS 
SELECT 'a', 1, 1 FROM DUAL 
UNION ALL SELECT 'b', 1, 1 FROM DUAL 
UNION ALL SELECT 'c', 1, 1 FROM DUAL 
UNION ALL SELECT 'd', 2, 1 FROM DUAL 
UNION ALL SELECT 'e', 3, 1 FROM DUAL 
UNION ALL SELECT 'f', 3, 1 FROM DUAL 
UNION ALL SELECT 'g', 4, 2 FROM DUAL 
UNION ALL SELECT 'h', 4, 2 FROM DUAL 
UNION ALL SELECT 'i', 5, 2 FROM DUAL 
UNION ALL SELECT 'j', 5, 2 FROM DUAL; 

クエリ1

SELECT DISTINCT 
    MIN(sal) KEEP (DENSE_RANK FIRST ORDER BY sal) OVER (PARTITION BY deptno) AS min_sal_first_sal, 
    MAX(sal) KEEP (DENSE_RANK FIRST ORDER BY sal) OVER (PARTITION BY deptno) AS max_sal_first_sal, 
    MIN(name) KEEP (DENSE_RANK FIRST ORDER BY sal) OVER (PARTITION BY deptno) AS min_name_first_sal, 
    MAX(name) KEEP (DENSE_RANK FIRST ORDER BY sal) OVER (PARTITION BY deptno) AS max_name_first_sal, 
    MIN(name) KEEP (DENSE_RANK LAST ORDER BY sal) OVER (PARTITION BY deptno) AS min_name_last_sal, 
    MAX(name) KEEP (DENSE_RANK LAST ORDER BY sal) OVER (PARTITION BY deptno) AS max_name_last_sal, 
    deptno 
FROM test 

Results:あなたの例では

| MIN_SAL_FIRST_SAL | MAX_SAL_FIRST_SAL | MIN_NAME_FIRST_SAL | MAX_NAME_FIRST_SAL | MIN_NAME_LAST_SAL | MAX_NAME_LAST_SAL | DEPTNO | 
|-------------------|-------------------|--------------------|--------------------|-------------------|-------------------|--------| 
|     1 |     1 |     a |     c |     e |     f |  1 | 
|     4 |     4 |     g |     h |     i |     j |  2 | 
2

ご集合体はあなたがソートされている同じ列にあるので、違いは、ありません。 "KEEP"の実際のポイント/パワーは、異なるカラムを集計して並べ替えるときです。例えば(他の答えから "テスト"テーブルを借りる)...

SELECT deptno, min(name) keep (dense_rank first order by sal desc, name ) , 
max(sal) 
FROM test 
group by deptno 

;

このクエリは、各部門で最高の給与を持つ人の名前を取得します。 「KEEP」句なしに代替を考える:

SELECT deptno, name, sal 
FROM test t 
WHERE not exists (SELECT 'person with higher salary in same department' 
              FROM test t2 
              WHERE t2.deptno = t.deptno 
              and (( t2.sal > t.sal) 
              OR (t2.sal = t.sal AND t2.name < t.name))) 

KEEP句がより簡単で効率的である(34は、この単純な例では、代替のために取得する対のみ3一貫性が得ます)。

0

2つの列に基づいて順序付けし、その列の一方または両方をフェッチする場合にも役立ちます。

CREATE TABLE test (name, sal, deptno) AS 
SELECT 'adam', 100, 1 FROM DUAL 
UNION ALL SELECT 'bravo', 500, 1 FROM DUAL 
UNION ALL SELECT 'coy', 456, 1 FROM DUAL 
UNION ALL SELECT 'david', 50, 1 FROM DUAL 
UNION ALL SELECT 'ethan', 50, 1 FROM DUAL 
UNION ALL SELECT 'feral', 300, 1 FROM DUAL; 

は今、次の2人の従業員が同じ最低給料を持っている場合は、名前が最初のアルファベット順で来ると一つにフェッチされた最低給与の従業員だけでなく、person.Conditionの給与を選択します。

select o.deptno 
,min(o.sal) keep 
    (dense_rank first order by o.sal, o.name) least_salary 
,min(o.name) keep 
    (dense_rank first order by o.sal, o.name) least_salary_person 
from test o 
    group by 
o.deptno; 

OUTPUT:

DEPTNO LEAST_SALARY LEAST_SALARY_PERSON

1 50デビッド