2012-03-28 5 views
3

次の2つの非常によく似た文がサブクエリにあります。 **との違いを強調しました。これらのクエリを推論できないOracleは同じです - 非常に異なる実行計画が生成されました

1:

SELECT DISTINCT name 
FROM person, nameindex n 
WHERE person.id1 ='0812' 
AND person.id2 =n.id2 
AND person.id1 =n.id1 
AND n.phonetic IN 
    (SELECT n2.phonetic 
    FROM nameindex n2 
    WHERE n2.id1=person.id1 ** 
    GROUP BY n2.phonetic 
HAVING COUNT(*) BETWEEN 4 AND 500) 

2:

SELECT DISTINCT name 
FROM person, nameindex n 
WHERE person.id1 ='0812' 
AND person.id2 =n.id2 
AND person.id1 =n.id1 
AND n.phonetic IN 
    (SELECT n2.phonetic 
    FROM nameindex n2 
    WHERE n2.id1='0812' ** 
    GROUP BY n2.phonetic 
HAVING COUNT(*) BETWEEN 4 AND 500) 

私はperson.id1は、サブクエリで0812一定でなければならないことを、Oracleが推測できると思います。ただし、両方のクエリでは、実行計画とコストが大きく異なります(1:4404211855、2:cost:36237)。どうしてこれなの?

これはOLTPではなく解析的なクエリであるため、この特定のクエリに対してインデックスは定義されていません。

(クエリの背景:= 4と500の出現の間にであるためnameindexテーブル内の表音エントリが「0812」をID1内の人物の名前を取得します。)私は似たを実行した

+0

両方のバージョンでWHEREが見つかりません。 –

+0

SQL Serverでは、クエリの両方のバージョンで、 '0812'の述語が 'n2'にプッシュダウンされ、計画が同一であるので、Oracle *がこれを推測できますが、あなたの質問から推測できません。 –

+0

Person.id1とnameindex.id1のタイプを教えてください。数字? varchars? –

答えて

4

以下の設定を使用してクエリは:

PLAN_TABLE_OUTPUT 
-------------------------------------------------------------------------------- 
Plan hash value: 291343677 
-------------------------------------------------------------------------------- 
| Id | Operation    | Name  | Rows | Bytes | Cost (%CPU)| Time 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |   |  1 | 2040 | 331K (2)| 01:06:2 
| 1 | HASH UNIQUE   |   |  1 | 2040 | 331K (2)| 01:06:2 
|* 2 | FILTER    |   |  |  |   | 
|* 3 | HASH JOIN   |   | 891 | 1775K| 1750 (2)| 00:00:2 
|* 4 |  TABLE ACCESS FULL | NAMEINDEX | 892 | 18732 | 739 (2)| 00:00:0 
|* 5 |  TABLE ACCESS FULL | PERSON | 1395 | 2750K| 1010 (2)| 00:00:1 
|* 6 | FILTER    |   |  |  |   | 
| 7 |  HASH GROUP BY  |   | 9550 | 76400 | 740 (2)| 00:00:0 
|* 8 |  TABLE ACCESS FULL| NAMEINDEX | 9550 | 76400 | 739 (2)| 00:00:0 
-------------------------------------------------------------------------------- 
Predicate Information (identified by operation id): 
--------------------------------------------------- 
    2 - filter(EXISTS (SELECT 0 FROM "NAMEINDEX" "N2" WHERE "N2"."ID1"=:B1 
       GROUP BY "N2"."PHONETIC" HAVING "N2"."PHONETIC"=:B2 AND COUNT(*)>= 
       COUNT(*)<=500)) 
    3 - access("PERSON"."ID2"="N"."ID2" AND "PERSON"."ID1"="N"."ID1") 
    4 - filter("N"."ID1"='0812') 
    5 - filter("PERSON"."ID1"='0812') 
    6 - filter("N2"."PHONETIC"=:B1 AND COUNT(*)>=4 AND COUNT(*)<=500) 
    8 - filter("N2"."ID1"=:B1) 

はあなたが半結合にrewritteで見ることができるように:

CREATE TABLE person (id1, id2, NAME) AS 
    SELECT to_char(mod(ROWNUM, 1000), 'fm0000'), ROWNUM, 
      dbms_random.string('A',10) 
    FROM dual 
    CONNECT BY LEVEL <= 1e6; 
CREATE TABLE nameindex (id1, id2, phonetic) AS 
    SELECT id1, id2, to_char(dbms_random.value(1, 200), 'fm000') 
    FROM person; 

私はあなたの最初のクエリは、次の計画を生成することを発見しましたここ

SELECT DISTINCT NAME 
    FROM person, nameindex n 
WHERE person.id1 = '0812' 
    AND person.id2 = n.id2 
    AND person.id1 = n.id1 
    AND EXISTS (SELECT NULL 
       FROM nameindex n2 
       WHERE n2.id1 = person.id1 
        AND n2.phonetic = n.phonetic 
       GROUP BY n2.phonetic 
       HAVING COUNT(*) BETWEEN 4 AND 500); 

あなたはサブクエリは一定ではなく、したがって、最適な実行未満に至る、主クエリの各行について計算されていることを確認:Nこのクエリと同じプランを生成する存在します計画。

集約セミジョインを使用するときは、GROUP BY内のすべての結合列を使用することをお勧めします。次のクエリは、最適なプランを生成します。

SELECT DISTINCT NAME 
    FROM person, nameindex n 
WHERE person.id1 = '0812' 
    AND person.id2 = n.id2 
    AND person.id1 = n.id1 
    AND (n.id1, n.phonetic) IN (SELECT n2.id1, n2.phonetic 
           FROM nameindex n2 
           GROUP BY n2.id1, n2.phonetic 
           HAVING COUNT(*) BETWEEN 4 AND 500); 
+0

これはうまく動作し、1つのパラメータしか使用しません。オラクルのオプティマイザは、思ったよりもスマートではないようです。 – stracktracer

+0

人間の介入は、特別なコーナーケースではまだ必要ですが、正直言って私は悪いことではありません:) –

関連する問題