9

クエリ 1 では、id がインデックス付きの列であるにもかかわらず、フル テーブル スキャンが実行されています。クエリ 2 は同じ結果を達成しますが、はるかに高速です。クエリ 1 を実行してインデックス付きの列を返す場合、クエリはすぐに返されますが、インデックスのない列が返されるか、行全体が返される場合は、クエリに時間がかかります。

クエリ 3 では高速に実行されますが、列 'code' は NUMBER(12) ではなく VARCHAR2(10) であり、'id' と同じ方法でインデックスが作成されます。

クエリ 1 がインデックスを使用する必要があることを認識しないのはなぜですか? インデックス付きの数値列をより速く実行できるようにするために変更する必要があるものはありますか?

【クエリ1】

select a1.*
from people a1
where a1.id like '119%' 
and rownum < 5

Explain Plan
SELECT STATEMENT ALL_ROWS
コスト: 67 バイト: 2,592 カーディナリティ: 4
2 COUNT STOPKEY
    1 TABLE ACCESS FULL TABLE people
     コスト: 67 バイト: 3,240 カーディナリティ: 5

【クエリ2】

select a1.*
from people a1, people a2
where a1.id = a2.id
and a2.id like '119%' 
and rownum < 5

Explain Plan
SELECT STATEMENT ALL_ROWS
コスト: 11 バイト: 2,620 カーディナリティ: 4
5 COUNT STOPKEY
    4 TABLE ACCESS BY INDEX ROWID TABLE people
    コスト: 3 バイト: 648 カーディナリティ: 1
        3 NESTED LOOPS
        コスト: 11 バイト: 2,620 カーディナリティ: 4
            1 INDEX FAST FULL SCAN INDEX people_IDX3
            コスト: 2 バイト: 54,796 カーディナリティ: 7,828
            2 INDEX RANGE SCAN INDEX people_IDX3
            コスト: 2 カーディナリティ: 1

【クエリ3】

select a1.*
from people a1
where a1.code like '119%' 
and rownum < 5

Explain Plan
SELECT STATEMENT ALL_ROWS
コスト: 6 バイト: 1,296 カーディナリティ: 2
   3 COUNT STOPKEY
      2 TABLE ACCESS BY INDEX ROWID TABLE people
      コスト: 6 バイト: 1,296 カーディナリティ: 2
         1 INDEX RANGE SCAN INDEX people_IDX4
         コスト: 3 カーディナリティ: 2

4

5 に答える 5

14

LIKEパターンマッチング条件では、文字タイプが左側と右側の両方のオペランドとして表示されることを想定しています。NUMBERに遭遇すると、暗黙的にcharに変換します。クエリ1は、基本的に次のようにサイレントに書き直されます。

SELECT a1.*
  FROM people a1
 WHERE TO_CHAR(a1.id) LIKE '119%'
   AND ROWNUM < 5

それはあなたの場合に起こります、そしてそれは2つの理由で悪いです:

  1. 変換はすべての行に対して実行されますが、これは低速です。
  2. WHERE述語の関数(暗黙的ではありますが)が原因で、OracleはA1.ID列の索引を使用できません。

これを回避するには、次のいずれかを実行する必要があります。

  1. 列に関数ベースのインデックスを作成します。A1.ID

    CREATE INDEX people_idx5 ON people (TO_CHAR(id));

  2. ID列の最初の3文字のレコードを照合する必要がある場合は、これらの3文字だけを含むタイプNUMBERの別の列を作成し、プレーン=演算子を使用します。

  3. タイプのの列を作成し、それをで埋めます。インデックスを付けて、あなたの状態の代わりに使用してください。ID_CHARVARCHAR2TO_CHAR(id)IDWHERE

    もちろん、既存のID列に基づいて追加の列を作成する場合は、これら2つの同期を維持する必要があります。これは、単一のUPDATEとして、またはON-UPDATEトリガーでバッチで行うか、その列を適切なものに追加できます。コード内のINSERTおよびUPDATEステートメント。

于 2009-11-05T01:22:56.997 に答える
4

LIKEは文字列関数であるため、数値インデックスを簡単に使用することはできません。数値インデックスには、119,120,130、..、1191,1192,1193 ...、11921,11922 ...などがあります。つまり、「119」で始まるすべての行が同じ場所に配置されることはありません。したがって、インデックス全体を読み取る必要があります(したがって、FAST FULL SCAN)。文字ベースのインデックスでは、それらは一緒になります(たとえば、「119」、「1191」、「11911」、「120」など)。そのため、より優れた範囲スキャンを使用できます。

特定の範囲(たとえば、119000〜119999)のID値を探している場合は、それを述語(119000〜119999のID)として指定します。

于 2009-11-04T21:49:12.730 に答える
1

オプティマイザは、おそらく実際のレコード数が少ないために、テーブルスキャンを実行する方が高速であると判断しました。

また、非正確な一致は常に正確よりもはるかに悪いことを知っておく必要があります。whereが"a1.id= '123456'"の場合、おそらくインデックスを使用します。しかし、繰り返しになりますが、インデックスでさえ2回の読み取り(最初にインデックスでレコードを検索し、次にテーブルからブロックを読み取る)を取り、非常に小さいテーブルの場合はテーブルスキャンを決定できます。

于 2009-11-04T19:42:40.723 に答える
0

クエリの1つにヒントを配置して、目的のインデックスを使用するように強制してから、計画を確認してください。オプティマイザーインデックスを考慮に入れている可能性がありますが、知覚されるコスト。

于 2009-11-04T19:39:24.350 に答える
-3

キーワードは、LIKE正規表現の一致を実行していることをSQLに通知します。使用可能な文字列関数をチェックして、クエリを単純に表現できるかどうかを確認するまでは、SQLやプログラミングライブラリで正規表現を使用しないでください。この場合、コードの最初の3文字で構成される部分文字列を比較するだけで、これを等しい条件に変更できます。Oracleでは、これは次のようになります。

SELECT *
FROM people
WHERE SUBSTR(code,1,3) = '119'
于 2009-11-04T19:58:33.150 に答える