1

改善しようとしている非常に遅くて単純なクエリがあります。その目的は、ユニットで初めて失敗した日付を取得することであり、次のとおりです。

select unit_id, min(fail_Date) fail_Date
    from failures
  having min(fail_date) between '24-aug-2012' and '25-aug-2012'
   group by unit_id

Unit_idは一意ではないことが推測されるかもしれませんが、これは障害が発生したユニットを識別するための外部キーです。この表には、同じユニットが複数回障害が発生した場合の複数のレコードがあります。

これが最善の方法ではないことはわかっていますが、テーブルを制御することはできず、そのまま使用する必要があります。

このテーブルには、fail_dateとunit_idのインデックスがあります。

それにもかかわらず、このクエリには10秒かかります。どうすれば高速化できるのでしょうか?しかし、それから私は値を照会するためにこの方法に来ました、そしてそれは0.03秒しかかかりません:

with fail_dates as
 (select unit_id, fail_date
    from failures
   where fail_date between '24-aug-2012' and '25-aug-2012')
select f.unit_id, min(f.fail_Date) fail_Date
  from fail_dates
 inner join failures f
    on fail_dates.unit_id= f.unit_id
 group by f.unit_id,fail_dates.fail_date
 having  min(f.fail_Date) = fail_dates.fail_date

どちらもまったく同じレコードを返しますが、2番目のレコードは10倍高速です。私の質問は、この2つのクエリは本当に同等ですか?そして、なぜ2番目の方がはるかに速いのですか?

ありがとう!

4

3 に答える 3

3

私の結論は変わり、この答えは大幅に書き直されました。

最初は2つのクエリが違うと思いましたが、コメントを読んで2つ目のクエリを再検討したところ、実際に同じ結果が得られることがわかりました。どちらのクエリも、最も早い障害が2日の範囲内にあるユニットのみを返します。

最初のクエリは、各ユニットのすべての障害日を論理的に確認する必要があるため、低速です。テーブル全体(またはインデックス)のスキャンを実行している可能性があります。

2番目のクエリは、ターゲットの日付範囲内で障害が発生したユニットの最小障害日のみを計算するため、はるかに高速です。目標範囲内で障害が発生したユニットを特定するために、前縁に障害日が設定されたインデックスを使用していると思います。次に、リーディングエッジにユニットIDを持つインデックスを使用して、関連するユニットの最小失敗日を探すことができます。

次のクエリはクエリと同等である必要があり、2番目のクエリよりも少し速くなる可能性がありますが、私はそれを当てにしません。このクエリでは、失敗日が早いレコードが見つかるとすぐにユニットを削除できるため、より高速になる可能性がありますが、2番目のクエリでは、ユニットのすべての失敗日を論理的に確認する必要があります。このクエリが2番目のクエリよりも遅くない限り、ロジックがより単純でわかりやすいと思うので、これを選択します。

select unit_id,
       min(fail_Date) fail_Date
  from failures f
 where fail_date between '24-aug-2012' and '25-aug-2012'
   and not exists (
          select 1
            from failures f2
           where f2.unit_id=f1.unit_id
             and f2.fail_date < '24-aug-2012'
       )
 group by unit_id
于 2012-08-25T00:15:13.043 に答える
2

元のクエリを次のように書き直せば改善できると思います

select unit_id, min(fail_Date) fail_Date
  from failures
  where fail_date between '24-aug-2012' and '25-aug-2012'
  group by unit_id

これは、2番目のクエリとほぼ同じように実行されると思います。このクエリと2番目のクエリがより高速に実行される理由は、最初に、目的の範囲にFAIL_DATEがあるレコードのみを選択してテーブルをサブセット化するためです。これにより、おそらくインデックスを使用できるようになります。元のクエリは、対象のレコードのみを選択するのに役立つWHERE句がないため、テーブル全体をスキャンする必要があります。

2つのクエリのEXPLAINPLAN出力を調べて、それらがどのように評価されているかを確認することをお勧めします。

共有してお楽しみください。

于 2012-08-24T22:13:18.200 に答える
1

それらをあまり詳しく見なくても、現在、同じ行を返すという意味で同等であると言えます。続行するためにそれを期待しないでください。

ただし、2番目のバージョンの共通テーブル式(CTE)には、非常に制限的なWHERE句があり、2日間だけ行を選択します。その制限的なWHERE句は、GROUPBYとHAVINGが実行する必要のある作業を大幅に削減するはずです。

実行計画(pdf、ホワイトペーパー)を見れば、確実にわかるはずです。

于 2012-08-24T22:12:15.253 に答える