最近、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 –コードの記述、美化、リファクタリングを簡単に行い、生産性を向上させます。