1

データベース

Table1
 Id
 Table2Id

...

Table2
  Id
  StartTime
  Duration  //in hours

クエリ

select * from Table1 join Table2 on Table2Id = Table2.Id 
where starttime < :starttime and starttime + Duration/24 > :endtime

現在、このクエリの実行には約 2 秒かかっていますが、これは長すぎます。id 列にはインデックスがあり、Start_time+duration/24 には関数インデックスがあります。Sql Developer では、クエリ プランはインデックスが使用されていないことを示しています。このクエリは、テストの開始時間と終了時間に対して 475 行を返します。テーブル 2 には最大 80 万行あります テーブル 1 には最大 20 万行あります

期間/24 の計算がクエリから削除され、静的な値に置き換えられた場合、クエリ時間は半分に短縮されます。これはまったく同じデータを取得するわけではありませんが、除算にはコストがかかると思われます。

(starttime + duration/24) が設定された Table2 に endtime 列を追加することもテストしました。この列は、単一の更新によって事前設定されました。本番環境で使用する場合は、更新トリガーを介して設定します。

select * from Table1 join Table2 on Table2Id = Table2.Id 
where starttime < :starttime and endtime > :endtime

このクエリは約 600 ミリ秒で実行され、結合にはインデックスが使用されます。冗長データを含む列が追加されるため、理想的とは言えません。

このクエリを高速化する方法はありますか?

4

3 に答える 3

3

starttimeと式の両方に関数インデックスを作成しますstarttime + Duration/24

create index myindex on table2(starttime, starttime + Duration / 24);

クエリの述語全体の複合インデックスを選択する必要がありますが、個別にインデックスを付けたオプティマイザは、これらのインデックスの1つのスキャンに基づくROWIDによる繰り返しのテーブルアクセスは、実際には全表スキャンよりも遅いと判断する可能性があります。

また、バインド変数にDATEを渡すようにして、varcharからdateへの暗黙的な変換を行っていないことを確認してください。

optimizer_index_cost_adjシステムパラメータを下げてみてください。デフォルトは100だと思います。これを10に設定して、インデックスが選択されているかどうかを確認してください。

開始時刻でテーブルを分割することを検討してください。

于 2009-06-02T18:16:19.240 に答える
1

where句の選択性があまり良くない場合、Oracleはインデックスを使用しません。返される行数がテーブル内の行の総数の何パーセントかである場合、インデックスが使用されます(Oracleはインデックスの読み取りとテーブルの読み取りのコストをカウントするため、パーセンテージは異なります)。

また、where句でインデックス列を変更すると、インデックスが無効になります。たとえば、UPPERCASE(some_index_column)は、some_index_columnのインデックスの使用を無効にします。これが、starttime + duration / 24>:endtimeがインデックスを使用しない理由です。

これを試していただけますか

select * from Table1 join Table2 on Table1.Id = Table2.Table1Id 
where starttime < :starttime and starttime  > :endtime - Duration/24

これにより、インデックスを使用できるようになり、追加の列は必要ありません。

于 2009-06-02T16:52:18.003 に答える
1

範囲述語 (より大きい/より小さい) を持つ 2 つの基準があります。インデックス レンジ スキャンは、インデックスのあるポイントで開始し、別のポイントで終了できます。

starttime と "Starttime+duration/24" の複合インデックスの場合、先頭の列が starttime で述語が "バインド値未満" であるため、インデックスの左端 (最も早い starttime) から開始し、範囲スキャンします。 starttime が制限に達する時点までのすべての行。これらの一致ごとに、バインド値に対するインデックスの "Starttime+duration/24" の計算値を評価し、行を渡すか拒否することができます。テーブル内のほとんどのデータが古いと思われるため、ほとんどのエントリの開始時刻が古いため、ほとんどのインデックスをスキャンすることになります。

"Starttime+duration/24" と starttime の複合インデックスの場合、先頭の列は関数であり、述語は "バインド値よりも大きい" ため、インデックスの途中から開始し、最後まで機能します。これらの一致ごとに、インデックスの starttime をバインド値に対して評価し、行を渡すか拒否することができます。渡された enddate が最近のものである場合、実際にスキャンされるインデックスの量ははるかに少ないと思います。

インデックスの 2 番目の列として starttime がなくても、"Starttime+duration/24" の既存の関数ベースのインデックスは引き続き有用であり、使用されるはずです。説明計画を調べて、bindvalue が日付であるか、日付に変換されていることを確認してください。変換する場合は、適切なフォーマット マスクが使用されていることを確認してください (たとえば、'1/Jun/09' の入力値は 0009 年に変換される可能性があるため、Oracle は条件を非常に緩和したものと見なし、インデックスを使用しない傾向があります。 - さらに、結果が間違っている可能性があります)。

「Sql Developerでは、クエリプランはインデックスが使用されていないことを示しています。」インデックスがtable2行を見つけるために使用されていなかった場合、オプティマイザはtable2のほとんど/すべてが返されると考えたのではないかと思います[明らかにそうではありません。あなたの番号]。ほとんどの table1 が返されると思いますが、どちらの述語も多くのフィルタリングを行いませんでした。上で述べたように、「未満」述語は選択的ではないと思いますが、「より大きい」述語は選択的であるべきです。説明計画、特に ROWS 値を見て、オラクルの考えを確認してください。

PS。値を調整するということは、オプティマイザーが見積もりの​​基準を変更することを意味します。ジャーニー プランナーが、平均速度 50 を想定しているので、旅行に 6 時間かかると言った場合、平均速度 100 を想定するように指示すると、3 時間になります。実際に移動する速度や、実際に移動するのにかかる時間には影響しません。したがって、その値を変更して、データベース (またはセッション) の実際の値をより正確に反映させたいだけです。

于 2009-06-03T03:44:26.717 に答える