2

私はこのクエリを持っています...

SELECT Distinct([TargetAttributeID]) FROM
    (SELECT distinct att1.intAttributeID as [TargetAttributeID]
        FROM AST_tblAttributes att1
        INNER JOIN
        AST_lnkProfileDemandAttributes pda
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID

    union all

    SELECT distinct ca2.intAttributeID as [TargetAttributeID] FROM
        AST_lnkCapturePolicyAttributes ca2
        INNER JOIN
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57
        WHERE ec2.dteCreatedDate >= @cutoffdate) x

上記のクエリの実行プラン

2つの内部の区別は、それぞれ32行と10,000行を見ています。このクエリは5行を返し、1秒以内に実行されます。

次に、このクエリの結果を同様の件名として使用するとIN...

SELECT attx.intAttributeID,attx.txtAttributeName,attx.txtAttributeLabel,attx.txtType,attx.txtEntity FROM
    AST_tblAttributes attx WHERE attx.intAttributeID 
    IN
    (SELECT Distinct([TargetAttributeID]) FROM
    (SELECT Distinct att1.intAttributeID as [TargetAttributeID]
        FROM AST_tblAttributes att1
        INNER JOIN
        AST_lnkProfileDemandAttributes pda
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID
    union all
    SELECT  Distinct ca2.intAttributeID as [TargetAttributeID] FROM
        AST_lnkCapturePolicyAttributes ca2
        INNER JOIN
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57
        WHERE ec2.dteCreatedDate >= @cutoffdate) x)

上記のクエリの実行プラン

それから3分以上かかります!クエリの結果を取得してIN「手動」で実行すると、非常に迅速に返されます。

しかし、2つの内側を削除するとDISTINCTS...。

SELECT attx.intAttributeID,attx.txtAttributeName,attx.txtAttributeLabel,attx.txtType,attx.txtEntity FROM
    AST_tblAttributes attx WHERE attx.intAttributeID 
    IN
    (SELECT Distinct([TargetAttributeID]) FROM
    (SELECT att1.intAttributeID as [TargetAttributeID]
        FROM AST_tblAttributes att1
        INNER JOIN
        AST_lnkProfileDemandAttributes pda
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID
    union all
    SELECT ca2.intAttributeID as [TargetAttributeID] FROM
        AST_lnkCapturePolicyAttributes ca2
        INNER JOIN
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57
        WHERE ec2.dteCreatedDate >= @cutoffdate) x)

上記のクエリの実行プラン

..それから1秒以内に戻ってきます。

SQL Serverは何を考えていますか?2つのサブクエリを実行し、その結果をのサブジェクトとして使用できることを理解できませんかIN。相関サブクエリと同じくらい遅いようですが、相関していません!!!

Show Estimate Executionプランには、それぞれ100%のコストで3つのクラスター化インデックススキャンがあります。(実行計画はこちら

DISTINCTS内部がこのクエリを非常に遅くする理由を誰かに教えてもらえますか(ただし、 IN...のサブジェクトとして使用された場合のみ)?

アップデート

申し訳ありませんが、これらの実行計画を立てるのに時間がかかりました...

クエリ1

クエリ2(遅いもの)

クエリ3-内部の区別なし

4

3 に答える 3

9

正直なところ、関係演算子に関しては、そこに無償のバロッククエリがあり、SQL Serverは、代替実行プランを見つけることができる時間内に、代替実行プランの検索を停止するという事実に帰着すると思います。

プランのコンパイルの解析およびバインドフェーズの後、SQL Serverは結果のツリーに論理変換を適用し、それぞれのコストを見積もり、コストが最も低いものを選択します。与えられたウィンドウ内で計算できるのと同じくらい多くの可能な変換を使い果たすわけではありません。したがって、おそらく、適切な計画に到達する前にそのウィンドウを焼き尽くしており、AST_tblAttributesに外側の半自己結合が追加されてエッジを超えました。

どのようにそれは無償でバロックですか?さて、最初に、これがあります(ノイズリダクションのために簡略化されています):

select distinct intAttributeID from (
   select distinct intAttributeID from AST_tblAttributes ....
   union all
   select distinct intAttributeID from AST_tblAttributes ....
   )

2つのセットを連結し、固有の要素を投影しますか?そのための演算子があることがわかりました、それはと呼ばれていUNIONます。したがって、計画のコンパイル中に十分な時間があり、十分な論理変換が行われると、SQLServerは実際の意味が次のようになることを認識します。

select intAttributeID from AST_tblAttributes ....
union
select intAttributeID from AST_tblAttributes ....

しかし、待ってください。これを相関サブクエリに入れます。相関サブクエリは半結合であり、正しい関係では半結合での論理的な重複排除は必要ありません。したがって、SQLServerはクエリを次のように論理的に書き直すことができます。

select * from AST_tblAttributes
where intAttributeID in (
  select intAttributeID from AST_tblAttributes ....
  union all
  select intAttributeID from AST_tblAttributes ....
  )

次に、物理的な計画の選択に取り掛かります。しかし、そこにたどり着くには、最初にがらくたを確認する必要があり、それは最適化ウィンドウの範囲外になる可能性があります。


編集:

実際、これを自分で調べ、上記の推測を裏付ける方法は、両方のバージョンのクエリを同じウィンドウに配置し、推定実行プランを並べて比較することです(SSMSではCtrl-L)。一方をそのままにして、もう一方を編集し、何が変わるかを確認します。

オプティマイザーを実行すると、いくつかの代替形式が論理的に同等であると認識されて同じ適切な計画を生成し、他の形式は最適ではない計画を生成することがわかります。**

次に、SQLServerがクエリを実行するために実行する実際の作業量を使用SET STATISTICS IO ONして監視できます。SET STATISTICS TIME ON

SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT ....
SELECT ....

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

出力はメッセージペインに表示されます。

**またはそうではない-それらがすべて同じ計画を生成するが、実際の実行時間はあなたが言うようにまだ変化する場合、何か他のことが起こっている可能性があります-それは前代未聞ではありません。実際の実行計画を比較して、そこから進んでください。

于 2012-06-12T17:15:01.940 に答える
2

エルロノコ

まず第一に可能な説明:

「このクエリは5行を返し、1秒以内に実行されます」と言います。しかし、ESTIMATEは何行返されますか?見積もりが大幅にずれている場合、IN部分の一部としてクエリを使用すると、インデックスを探す代わりに、外側の部分のAST_tblAttributes全体をスキャンする可能性があります(これは大きな違いを説明する可能性があります)

さまざまなバリアントのクエリプランを(ファイルとして)共有した場合、ここで内部で何が起こっているのかを知ることができるはずです。また、説明を検証することもできます。

于 2012-06-14T23:24:21.067 に答える
1

編集:各DISTINCTキーワードは、クエリプランに新しい並べ替えノードを追加します。基本的に、他のDISTINCTをそこに含めることで、SQLにテーブル全体を何度も再ソートさせて、重複が返されないようにします。このような各操作は、クエリのコストを4倍にすることができます。これは、意図しない意図で、DISTINCTオペレーターが持つ可能性のある効果の良いレビューです私自身、これに噛まれてきました。


SQL 2008を使用していますか?もしそうなら、これを試して、DISTINCT作業をCTEに入れてから、メインテーブルに参加することができます。CTEはかなり高速であることがわかりました。

WITH DistinctAttribID
AS
(
SELECT Distinct([TargetAttributeID]) 
FROM (
    SELECT distinct att1.intAttributeID as [TargetAttributeID] 
        FROM AST_tblAttributes att1 
        INNER JOIN 
        AST_lnkProfileDemandAttributes pda 
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID 

    UNION ALL 

    SELECT distinct ca2.intAttributeID as [TargetAttributeID] FROM 
        AST_lnkCapturePolicyAttributes ca2 
        INNER JOIN 
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57 
        WHERE ec2.dteCreatedDate >= @cutoffdate
) x

SELECT attx.intAttributeID,
    attx.txtAttributeName,
    attx.txtAttributeLabel,
    attx.txtType,
    attx.txtEntity 
FROM AST_tblAttributes attx 
JOIN DistinctAttribID attrib
    ON attx.intAttributeID = attrib.TargetAttributeID
于 2012-06-12T00:28:51.383 に答える