最近、データベースクエリを生成する1つのアプリケーションに出会いました。 誰も驚かないことは理解していますが、アプリケーションの速度が低下し始め、その理由を突き止めるタスクが与えられたとき、これらのリクエストを見つけて非常に驚きました。 SQL Serverで対処しなければならないことがあるのは次のとおりです。
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
オブジェクトの名前が変更されました。
最も印象的なのは、同じテーブルが何度も使用され、ブラケットの数がおかしいことです。 しかし、このコードが気に入らなかっただけでなく、SQL Serverも熱心ではなく、計画の作成に多くのリソースを費やしています。 リクエストには50〜150ミリ秒かかり、プランの作成には最大2.5秒かかります。 今日は、状況を修正するためのオプションを検討しません。私の場合、アプリケーション内のリクエストの生成を修正することは不可能だったと言います。
SQL Serverが長い間クエリプランを作成してきた理由を検討したいと思います。 どのDBMSでも、SQL Serverも例外ではありません。最適化の主な問題は、テーブルの相互接続方法です。 結合方法自体に加えて、テーブルを結合する順序は非常に重要です。
テーブルの結合順序について詳しく説明しましょう。 この問題では、理解することが非常に重要です- テーブル結合の可能な数は、線形ではなく指数関数的に増加します。 たとえば、2つのテーブルの場合、可能な接続オプションの数は2のみであり、3の場合、この数は12までになります。 接続順序が異なるとクエリコストも異なる可能性があり、SQL Serverオプティマイザーは「最適な」方法を選択する必要がありますが、テーブルが多くなると、これはリソースを大量に消費するタスクになります。 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があるクエリで目的の結果を達成できない場合、それをいくつかの小さなクエリに分割すると、結果がどれほど優れているかに驚くでしょう。
PSもちろん、テーブルの結合順序の決定に加えて、クエリオプティマイザーは使用する接続の種類、データへのアクセス方法(スキャン、シーク)なども選択する必要があることを理解する必要があります。