sql >> データベース >  >> RDS >> Database

複数のJOINがクエリに適していない、またはオプティマイザの邪魔にならない理由

    最近、DBクエリを生成するアプリケーションに出くわしました。それについては何も新しいことはないことは理解していますが、アプリケーションの実行が遅くなり、速度が低下した理由を突き止めなければならなかったとき、これらのクエリを見つけて驚いていました。 SQLServerが時々対処しなければならないことは次のとおりです。

    SELECT COUNT(DISTINCT "pr"."id") FROM  ((((((((((((((((("SomeTable" "pr"
    LEFT OUTER JOIN "SomeTable1698" "uf_pr_id_698" ON "uf_pr_id_698"."request" = "pr"."id") 
    LEFT OUTER JOIN "SomeTable1700" "ufref3737_i2" ON "ufref3737_i2"."request" = "pr"."id") 
    LEFT OUTER JOIN "SomeTable1666" "x0" ON "x0"."request" = "ufref3737_i2"."f6_callerper")
    LEFT OUTER JOIN "SomeTable1666" "uf_ufref4646_i3_f58__666" ON "uf_ufref4646_i3_f58__666"."request" = "ufref3737_i2"."f58_")
    LEFT OUTER JOIN "SomeTable1694" "x1" ON "x1"."request" = "ufref3737_i2"."f38_servicep")
    LEFT OUTER JOIN "SomeTable3754" "ufref3754_i12" ON "pr"."id" = "ufref3754_i12"."request")
    LEFT OUTER JOIN "SomeTable1698" "uf_ufref3754_i12_reference_698" ON "uf_ufref3754_i12_reference_698"."request" = "ufref3754_i12"."reference")
    LEFT OUTER JOIN "SomeTable1698" "x2" ON "x2"."request" = "ufref3737_i2"."f34_parentse")
    LEFT OUTER JOIN "SomeTable4128" "ufref3779_4128_i14" ON "ufref3737_i2"."f34_parentse" = "ufref3779_4128_i14"."request")
    LEFT OUTER JOIN "SomeTable1859" "x3" ON "x3"."request" = "ufref3779_4128_i14"."reference")
    LEFT OUTER JOIN "SomeTable3758" "ufref3758_i15" ON "pr"."id" = "ufref3758_i15"."request")
    LEFT OUTER JOIN "SomeTable1698" "uf_ufref3758_i15_reference_698" ON "uf_ufref3758_i15_reference_698"."request" = "ufref3758_i15"."reference")
    LEFT OUTER JOIN "SomeTable3758" "ufref3758_i16" ON "pr"."id" = "ufref3758_i16"."request")
    LEFT OUTER JOIN "SomeTable4128" "ufref3758_4128_i16" ON "ufref3758_i16"."reference" = "ufref3758_4128_i16"."request")
    LEFT OUTER JOIN "SomeTable1859" "x4" ON "x4"."request" = "ufref3758_4128_i16"."reference")
    LEFT OUTER JOIN "SomeTable4128" "ufref4128_i17" ON "pr"."id" = "ufref4128_i17"."request")
    LEFT OUTER JOIN "SomeTable1859" "uf_ufref4128_i17_reference_859" ON "uf_ufref4128_i17_reference_859"."request" = "ufref4128_i17"."reference")
    LEFT OUTER JOIN "SomeTable1666" "uf_ufref4667_i25_f69__666" ON "uf_ufref4667_i25_f69__666"."request" = "uf_pr_id_698"."f69_"
    WHERE ("uf_pr_id_698"."f1_applicant" IN (248,169,180,201,203,205,209,215,223,357,371,379,3502,3503,3506,3514,3517,3531,3740,3741)
    OR "x0"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR "uf_ufref4646_i3_f58__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR "uf_ufref4667_i25_f69__666"."f24_useracco" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 111)  AND "ufref3737_i2"."f96_" = 0   AND (("ufref3737_i2"."f17_source"  Is Null OR "ufref3737_i2"."f17_source"  <> 566425)
    AND ("ufref3737_i2"."f17_source"  Is Null OR "ufref3737_i2"."f17_source"  <> 566424)  OR ("uf_pr_id_698"."f10_status" Is Null OR "uf_pr_id_698"."f10_status" <> 56) )
    AND ("uf_pr_id_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR "x1"."f19_restrict" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR "uf_ufref3754_i12_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR "x2"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR "x3"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) 
    OR "uf_ufref3758_i15_reference_698"."f12_responsi" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136) 
    OR "x4"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136)
    OR "uf_ufref4128_i17_reference_859"."f5_responsib" IN (578872,564618,565084,566420,566422,566936,567032,567260,567689,579571,580813,594452,611522,611523,615836,621430,628371,633044,634132,634136))
    AND ("uf_pr_id_698"."f12_responsi"  Is Null OR "uf_pr_id_698"."f12_responsi"  <> 579420)  ) AND "pr"."area" IN (700) AND "pr"."area" IN (700) AND "pr"."deleted_by_user"=0 AND "pr"."temporary" = 0

    オブジェクトの名前が変更されました。

    最も印象的なのは、同じテーブルが複数回使用され、括弧の数が単純に私を夢中にさせたことです。このコードが気に入らなかったのは私だけではありませんでした。SQLServerもこのコードを評価せず、計画を立てるために多くのリソースを費やしました。クエリは50〜150ミリ秒実行され、プランの作成には最大2.5ミリ秒かかる場合があります。今日は、問題を修正する方法については検討しませんが、1つだけ説明します。私の場合、アプリケーションでクエリの生成を修正することは不可能でした。

    代わりに、SQLServerが長い間クエリプランを作成する理由を分析したいと思います。 SQL Severを含むすべてのDBMSでは、主な最適化の問題は、テーブルを相互に結合する方法です。結合方法に加えて、テーブル結合の順序は非常に重要です。

    テーブル結合のシーケンスについて詳しく説明しましょう。テーブル結合の可能な数は、直線的ではなく、指数関数的に増加することを理解することが非常に重要です。 Foxの例では、2つのテーブルを結合する方法は2つしかなく、3つのテーブルで12のメソッドに達する可能性があります。結合シーケンスが異なればクエリコストも異なる可能性があり、SQLServerオプティマイザは最適な方法を選択する必要があります。ただし、テーブルの数が多いと、リソースを大量に消費するタスクになります。 SQL Serverがすべての可能なバリアントの調査を開始した場合、そのようなクエリは実行されない可能性があります。そのため、SQL Serverはそれを実行せず、常に最良の計画ではなく、非常に優れた計画を探します。 SQL Serverは常に、実行時間と計画の品質の間で妥協点を見つけようとします。

    これは、結合メソッドの指数関数的成長の例です。 SQL Serverは、さまざまな結合方法(左の深さ、右の深さ、ふさふさした木)を選択できます。視覚的には、次のように見えます。

    次の表は、テーブルの数が増えたときに可能な結合方法を示しています。

    これらの値は自分で取得できます:

    左深さの場合: 5! =5 x 4 x 3 x 2 x 1 =120

    ふさふさした木: (2n–2)!/(n–1)!

    結論 :JOINの数に特に注意し、オプティマイザーの邪魔にならないようにしてください。複数のJOINを含むクエリで目的の結果が得られない場合は、いくつかの小さなクエリに分割すると、結果に驚かれることでしょう。

    追記 もちろん、テーブル結合のシーケンスを定義するだけでなく、クエリオプティマイザは結合タイプ、データアクセス方法(スキャン、シーク)なども選択する必要があることを理解する必要があります。

    便利な製品:

    SQL Complete –コードの記述、美化、リファクタリングを簡単に行い、生産性を向上させます。


    1. データベースメール(SSMS)のデフォルトのパブリックプロファイルを設定する

    2. Unixエポックをタイムスタンプに変換する方法

    3. 行の値を連結するT-SQL

    4. SQLServerで中央値を計算する関数