2012-03-22 5 views
2

今日私は、追加の基準を追加することで修正できたSQLステートメントの問題に遭遇しましたが、なぜ私の変更によって問題が修正されたのかを知りたいのです。この基準でwhere句を変更すると、実行時間が大幅に短縮されるのはなぜですか?

問題クエリ:

SELECT * 
FROM 
    (SELECT ah.*, 
    com.location, 
    ha.customer_number, 
    d.name applicance_NAME, 
    house.name house_NAME, 
    dr.name RULE_NAME 
FROM actionhistory ah 
INNER JOIN community com 
ON (t.city_id = com.city_id) 
INNER JOIN house_address ha 
ON (t.applicance_id = ha.applicance_id 
AND ha.status_cd = 'ACTIVE') 
INNER JOIN applicance d 
ON (t.applicance_id = d.applicance_id) 
INNER JOIN house house 
ON (house.house_id = t.house_id) 
LEFT JOIN the_rule tr 
ON (tr.the_rule_id = t.the_rule_id) 
WHERE actionhistory_id >= 'ACT100010000' 
ORDER BY actionhistory_id 
) 
WHERE rownum <= 30000; 

SELECT * 
FROM 
    (SELECT ah.*, 
    com.location, 
    ha.customer_number, 
    d.name applicance_NAME, 
    house.name house_NAME, 
    dr.name RULE_NAME 
FROM actionhistory ah 
INNER JOIN community com 
ON (t.city_id = com.city_id) 
INNER JOIN house_address ha 
ON (t.applicance_id = ha.applicance_id 
AND ha.status_cd = 'ACTIVE') 
INNER JOIN applicance d 
ON (t.applicance_id = d.applicance_id) 
INNER JOIN house house 
ON (house.house_id = t.house_id) 
LEFT JOIN the_rule tr 
ON (tr.the_rule_id = t.the_rule_id) 
WHERE actionhistory_id >= 'ACT100010000' and actionhistory_id <= 'ACT100030000' 
ORDER BY actionhistory_id 
) 

"修正" _id列のすべては、インデックス付きの配列です。 最初の問合せの実行計画のコストは372で、2番目の問合せは14でした。これはOracle 11gデータベースで実行されています。

さらに、where句のactionhistory_idがACT100000000未満の場合、元のクエリは即時に戻ります。

答えて

3

これは、actionhistory_id列のインデックスが原因です。

最初の問合せでは、Oracleは 'ACT100010000'の後に来るレコードの索引を含むすべての索引ブロックを戻す必要があります。次に、索引を表に一致させてすべてのレコードを取得し、29999レコードを取得します。結果セット

2番目の問合せでは、Oracleは、 'ACT100010000'と 'ACT100030000'の間のレコードを含む索引ブロックのみを戻す必要があります。次に、索引ブロックで表されるレコードを表から取得します。最初のクエリを使用する場合よりも、インデックスを見つけた後にレコードを取得するそのステップでは、多くの作業が少なくなります。

IDがACT100000000より小さい場合、最後の行に気付く - これらのレコードはすべて同じメモリブロック(またはブロックの連続セット)にある可能性があります。

編集:私は実際のパフォーマンスについて話していましたが、彼はvarcharであるIDが潜在的な価値を(数字ではなく)大幅に増やしていると指摘しています。オプティマイザは実行までフルレンジを認識しないため、現実よりも時間がかかることがあります。あなたのポイントを考慮してさらに最適化するために、idカラムに関数ベースのインデックスを置くことができます。または、あるカラムのvarchar部分と別のカラムの数値部分の組み合わせキーにすることができます。

1
  • 両方のクエリの計画は何ですか?
  • テーブルの統計情報は最新のものですか?
  • 2つのクエリが同じ行セットを返しますか?それは明らかではありませんが、ACT100030000はシステム内で最大のactionhistory_idです。最初のクエリにはactionhistory_idの述語があり、TRA100010000の値は2番目のクエリのACTの値と大きく異なるため、少し混乱します。私はそれがタイプミスだと思いますか?
  • 最初の行を取得するのに必要な時間を測定していますか?または最後の行を取得するために必要な時間?これらの経過時間は何ですか?その情報のない私の推測では、あなたのactionhistory_id列に誤ったデータ型を使用しているように見えるという事実は、おそらくオプティマイザが選択性を過小評価する原因となっている適切な基数推定値を生成するために、Oracleオプティマイザの能力に影響を及ぼしていることである

あなたのプレディケートの数を減らし、パフォーマンスの低いプランを生成します。人間は、actionhistory_idACT10000で始まり、00001から30000まで30,000の連続した数値を持っているが、オプティマイザはそれほどスマートではないと推測することができます。それは13文字の文字列を見て、最後の10文字が常に数字になることを理解することができないので、256(8ビット文字を想定)ではなく10の可能な値しかなく、最初の8文字は常に同じ一定の値になる。一方、actionhistory_idNUMBERと定義され、1〜30000の値を持つ場合、オプティマイザが様々な述語の選択性について合理的な推定を行うことは劇的に容易になります。

関連する問題