2012-03-06 10 views
1

現在、特定の表に対して2つの索引を持つデータベースを使用しています。私が望むインデックスには、2つのカラム "Name"(varchar2)と "Time"(数字)があります。クエリを書き込むと、インデックスが使用されません(STARTVALUEとENDVALUEは数字です)。Stumped - 値が指定されても、Oracleは索引を使用しませんが、同じ値を戻す場合は、同じ値を返します。

SELECT SOMETHING 
    FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
    AND TIME BETWEEN STARTVALUE AND ENDVALUE 

しかし、代わりに次のクエリを使用すると

SELECT SOMETHING 
    FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
    AND TIME BETWEEN MY_FUNC('STARTQUAL') AND MY_FUNC('ENDQUAL') 

があります。

唯一の違いは、MY_FUNCがNUMBER型の値を明示的に返していることです。クエリオプティマイザが明示的に指定されたSTARTVALUEおよびENDVALUEのデータ型について混乱している可能性があります。タイプの競合が原因であると述べた類似のスレッドがいくつか見られました)。

注:MY_FUNCによって返される

  1. 値は、私が最初のクエリに指定していますEXACTLY同じ値です。問題の

  2. 指数は間違いなく(全く問題)正しいインデックス使用すると、実行時間が速くそれがないとき桁違いにありません。

  3. 私は最初のクエリでクエリヒントを指定していて、インデックスの使用を拒否しています。

私が見落としている愚か/単純なものがなければならないことは知っていますが、私はそれを見ることができません。

ご協力いただきありがとうございます。ここで

+0

いくつかの質問に:OracleのDBのバージョンは何ですか? 'TIME <= STARTQUALとTIME> = ENDQUAL'を試しましたか?あなたはEXPLAIN PLANを印刷できますか? – Guru

+0

'BETWEEN'に' to_number(STARTQUAL) 'を追加してみてください。 'MY_FUNC'定義を貼り付けることはできますか? – Guru

+0

うわー、すばらしい返答をいただきありがとうございます...私は< and > =を使ってみましたが、違いはありません。説明計画の鍵は、RANGE SCANではなくSKIP SCANを実行していることです。 – user1251193

答えて

0
SELECT SOMETHING 
FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
AND TIME BETWEEN STARTVALUE AND ENDVALUE 

、あなたは文字列(コメントによる)ですNUMBERでTIME、そしてSTARTVALUEENDVALUEを持っています。そのため、暗黙的な変換が行われている - つまり、あなたのクエリが効果的である:

SELECT SOMETHING 
FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
AND TO_CHAR(TIME) BETWEEN STARTVALUE AND ENDVALUE 

あなたはTO_CHAR(TIME)にファンクション索引を持っていない限り、それはインデックスを使用しません。

したがって、あなたは常に文字列パラメータは、数値に変換可能であることを期待しているのOracleを伝える必要がありますすなわち:

SELECT SOMETHING 
FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
AND TIME BETWEEN TO_NUMBER(STARTVALUE) AND TO_NUMBER(ENDVALUE) 

(それはとにかく、特にクエリでは、暗黙の型変換を避けるために、常に良い習慣だ)

+0

STARTVALUEとENDVALUEは文字列ではありません。具体的にはNUMBERSです。 STARTQUALとENDQUALは文字列です。これらの値は、現在提供しようとしているものと同じ値を効果的に引き出す関数に渡される値です(これは、これらを生成するアプリケーションでコードによって置き換えられた範囲を指定する古い方法でした値は直接)。とにかくありがとう... – user1251193

+0

申し訳ありませんuser1251193、私はあなたの専門知識のレベルを知らない。私は、実際に「この文字列には数値の表現が含まれている」という意味の「これは数字です」と言う人々に出くわしました。とにかく、コメントには「TO_NUMBER(STARTVALUE)」と記載されています。これはSTARTVALUEが文字列の場合にのみ意味があります。 –

2

また、問合せでリテラル値またはバインド値が使用されているかどうかによって、Oracleは問合せを異なる方法で最適化できます。

SELECT SOMETHING 
    FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
    AND TIME BETWEEN 7 AND 41; 
私はOracleは TIME列内のデータの分布について何かを知っている、との推測をしている賭ける

- おそらく時代遅れの統計を使用 - などにどのようなことの行とブロック(すなわち、選択)の割合列はです。その列にヒストグラムがあるかどうかを確認します。

しかし、このようなクエリ:OracleはMY_FUNC('7')が何をするかわからないので

SELECT SOMETHING 
    FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
    AND TIME BETWEEN :some_bind AND :some_other_bind; 

- あるいはMY_FUNC('7')がすること:

SELECT SOMETHING 
    FROM MYTABLE 
WHERE NAME = 'SOME-NAME' 
    AND TIME BETWEEN MY_FUNC('7') AND MY_FUNC('41'); 

はと同じ意味として最適化される可能性が高いですOracleに関数の決定論的なことを話していない限り、常に7の同じ値を返します。だから私の経験では、Oracleは大部分のために暗闇の中で刺すようになり、高いクラスタリング係数を持つインデックスを好む傾向があります。索引が最良の選択肢ではないとしても、少なくとも、できるだけ少ないデータ・ブロックを訪問することによって、下落リスクを最小限に抑えることができると推測されるようである。

私の推薦は、それが異なる動作をだ、なぜあなた自身のために見つけることです - 各クエリの10053トレースを取る:開始する

alter session set events = '10053 trace name context forever; 
run sql 
alter session set events = '10053 trace name context off; 
+0

それは私が得ようとしているロジックのタイプですが、私はそれを説明するためにそのレベルでオラクルについて十分に知りません。私はトレースと統計を見て、それがどんな光を放つかどうかを見ます。ありがとう! – user1251193

+0

アダム、私は、セッション全体のトレースメカニズムを見直しました。とても面白いようです。私はそれを実行しようとしましたが、.trcファイルを作成していないようです。私はこれがおそらくもう少し複雑であることを知っていますが、あなたがトレースを表示するために推薦できるポインタがあれば、私はそれを感謝するでしょう - そうでなければ、私は掘り続けるでしょう。再度、感謝します。 – user1251193

+0

トレースファイルは、データベースサーバ上の初期化パラメータ「USER_DUMP_DEST」によって識別されるディレクトリに作成されます。それらはクライアント上に作成されません。 –

関連する問題