2011-01-19 17 views
4

私は多くのクエリを連結して、すべてのクエリの最初の列が一定であるUNION ALLを使用して巨大なビューを持っています。ビューで実行するようにクエリを切り替える

CREATE VIEW M AS (
    SELECT 'A' ID, Value FROM A 
    UNION ALL 
    SELECT 'B' ID, Value FROM B 
    ... 
) 

クエリ

は、実際に、より複雑であるが、ここでの目的は次のように実行するどのようなクエリーをスイッチオンするだけです:実行計画は、クエリが一致しないということを示している

SELECT * FROM M WHERE ID = 'A' 

IDは実行されません。

私は、これは同じビューを通して異なるが類似したものを照合するのに使うことができる、本当にいいと思った。

SELECT CONVERT(char(4), 'T ') EntityTypeID, SystemID, TaskId EntityID 
FROM [dbo].[Task] 

UNION ALL 

SELECT CONVERT(char(4), 'T ') EntityTypeID, s.SystemID, [dbo].[Task].TaskId EntityID 
FROM [dbo].[Task] 
INNER JOIN [dbo].[System] s ON s.MasterSystemID = [dbo].[Task].SystemID 
INNER JOIN SystemEntitySettings ON SystemEntitySettings.SystemID = s.SystemID 
    AND SystemEntitySettings.EntityTypeID = 'T ' 
    AND SystemEntitySettings.IsSystemPrivate = 0 

考える:

ここ
WITH M AS (
    SELECT 'A' ID, Value FROM A 
    UNION ALL 
    SELECT 'B' ID, Value FROM B 
    ... 
) 
SELECT * FROM M WHERE ID = 'A' 

は、実際のクエリの一部のサンプルです:

しかし、私はこのようなCTEに対して問い合わせる場合でも、より良い実行計画で終わりますよWHERE EntityTypeiD <> 'T'のようなものを実行した場合、最初のクエリは完全に無視されますが、実際の行は返されません。

CTEのケースでは、ビューからクエリを完全に排除できないのはなぜですか?私はこれまでのところ、私はパラメータで取引を除外ませんが、私はまた、クエリヒントを指定するいずれかの方法でdesiered効果をachiveことができますいくつかの興味深いことを観察してきました

はEDIT

(どうやらいずれかが行います)、または2番目の結合はIN述部としてフィルタのみであるため、再作成します。

INNER JOIN SystemEntitySettings ON SystemEntitySettings.SystemID = s.SystemID 
    AND SystemEntitySettings.EntityTypeID = 'T ' 
    AND SystemEntitySettings.IsSystemPrivate = 0 

は... ...

WHERE s.SystemID IN (
     SELECT SystemID 
     FROM dbo.SystemEntitySettings 
     WHERE EntityTypeID = 'T ' AND IsSystemPrivate = 0 
) 

なるが、次のクエリは、同じ問題を持っています。 JOINの操作にいくつかの方法が関係しているかのように見えます。

SELECT CONVERT(char(4), 'CF ') EntityTypeID, s.SystemID, [dbo].[CareerForum].GroupID EntityID 
FROM [dbo].[CareerForum] 
INNER JOIN [dbo].[Group] ON [dbo].[Group].GroupID = [dbo].[CareerForum].GroupID 
INNER JOIN [dbo].[System] s ON s.MasterSystemID = [dbo].[Group].SystemID 
WHERE s.SystemID IN (SELECT SystemID FROM dbo.SystemEntitySettings WHERE EntityTypeID = 'CF ' AND IsSystemPrivate = 0) 

再現

(追加は、[グループ]は、このクエリで起きているとのJOIN注)次のスクリプトは、問題を再現するために使用することができます。クエリのヒントを使用してクエリを実行する場合、またはビューがcte(desiered result)を使用して実行されている場合、実行計画が完全に異なることに注目してください。

CREATE DATABASE test_jan_20 

USE test_jan_20 

create table source (
    x int not null primary key, 
) 

insert into source values (1) 
insert into source values (2) 
insert into source values (3) 
insert into source values (4) 
insert into source values (5) 
insert into source values (6) 

create table other (
    y int not null primary key, 
) 

insert into other values (1) 
insert into other values (2) 
insert into other values (3) 
insert into other values (4) 
insert into other values (5) 
insert into other values (6) 

create view dummy AS (

    SELECT 'A' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 1 AND 2 

    UNION ALL 

    SELECT 'B' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 3 AND 4 

    UNION ALL 

    SELECT 'B' id, source.x, y 
    FROM SOURCE 
    INNER JOIN other ON y = source.x 
    INNER JOIN source s2 ON s2.x = y - 1 --i need this join for the issue to occur in the execution plan 
    WHERE source.x BETWEEN 5 AND 6 

) 
GO 

--this one fails to remove the JOIN, not OK 
SELECT * FROM dummy WHERE id = 'c' 

--this is OK 
SELECT * FROM dummy WHERE id = 'c' OPTION (HASH JOIN) --NOTE: any query hint seems to do the trick 

--this is OK 
; 
WITH a AS (

    SELECT 'A' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 1 AND 2 

    UNION ALL 

    SELECT 'B' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 3 AND 4 

    UNION ALL 

    SELECT 'B' id, source.x, y 
    FROM SOURCE 
    INNER JOIN other ON y = source.x 
    INNER JOIN source s2 ON s2.x = y - 1 --i need this join for the issue to occur in the execution plan 
    WHERE source.x BETWEEN 5 AND 6 

) 
SELECT * FROM a WHERE id = 'c' 
+0

どちらの場合でも文字列リテラルを使用していますか、またはパラメータを使用していますか?どちらの文字列リテラルでも、サーバー上のパラメータ化を強制的にオンにしていますか? –

+0

は、 'T'を選択するはずの組合クエリの両方の分岐ですか? – RichardTheKiwi

+0

@Martin - リテラルは強制的なパラメータ化をしません。 @cyberkiwi - これはクエリ全体の一部ですが、UNION ALLを使用してすべて連結された他の多くのクエリがありますが、これらの2つはどちらも 'T' –

答えて

1

実行計画は、ID決して 実行で一致していない クエリことを示しています。

定数 'A'を指定しているので正しいです。そのため、計画は特定の文字列 'A'に対して作成され、1つの部分を切り捨てます。

私がいる、またはそれはCTEの場合にはそうするときではなく、私の の質問は、なぜそれは、それは が ビューから完全にクエリを排除することができないということである問題?

あなたはそれがそうであると述べたと思いましたか? SP、関数、またはパラメータ化されたクエリのいずれかで、パラメータ化された方法で使用していると思います。これにより、さまざまなパラメータを取ることができる計画を作成する必要があります。そのため、一部を切り捨てることは問題になりません。

目的を達成するには、クエリオプティマイザにconstant値のクエリを提示する動的SQLを生成する必要があります。これは、Viewまたはインラインテーブル値関数を使用するかどうかに関係なく適用されます。

EDIT:

これら二つの形式再現性のほか、以下のがあなたと同様

select * from (SELECT * FROM dummy) y WHERE id = 'c' 

with a as (Select * from dummy) SELECT * FROM a WHERE id = 'c' 
+0

この感覚。私はしかし、クエリのヒント(MERGE | HASH | LOOP)を使用して実際に結果をより好きなように、またはIN述語を使用してJOINを再作成することを観察しました。私はこれを反映するために私の質問を更新します。 –

+0

それだけです!私は実際に答えを移動しました。なぜなら、これが私の問題を解決したからです。ネストされたサブクエリとサブクエリ式を使用して内部ジョインを書き直す必要がありました。ありがとう、トン! –

2

あなたのテストケースでは、これが起こっています。

ビューとクエリヒントまたはCTEのクエリでは、クエリオプティマイザが「矛盾の検出」を使用しています。実行計画のプロパティでは、OPTIMIZATION LEVELTRIVIALであることがわかります。広げた簡単な計画は、ちょうどthis articleの点8に示されている計画と同じです。

クエリヒントなしのビューでは、これは自動的にパラメータ化されます。これにより、矛盾の検出が蹴られるのを防ぐことができます。as covered here

+0

私はあなたがこの問題を発生させるためにJOINを追加する必要があると信じています。再現可能なテストケースを提供できないかどうかもわかります。 –

+0

私は孤立して問題を再現できません。私は少し質問を拡大しました。 –

+0

私は問題を再現することができました.Matrinを見て、なぜこれが起こっているのか知っているか教えてください。乾杯! –

1

最後の更新を働くよう、クエリがあまりにも最適化されています。

あなたはフィルタとしてc(または任意の気苦労する欠損値)を提供する場合は、この計画を持っています:

|--Compute Scalar(DEFINE:([Union1019]=[Expr1018], [Union1020]=[ee].[dbo].[source].[x], [Union1021]=[ee].[dbo].[other].[y])) 
     |--Compute Scalar(DEFINE:([Expr1018]='B')) 
      |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1022])) 
       |--Constant Scan 
       |--Clustered Index Seek(OBJECT:([ee].[dbo].[source].[PK__source__3BD019E5171A1207] AS [s2]), SEEK:([s2].[x]=[Expr1022]) ORDERED FORWARD) 

、次のように拡大し、一定のスキャンで:他に

<RelOp AvgRowSize="19" EstimateCPU="1.57E-07" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="0" LogicalOp="Constant Scan" NodeId="3" Parallel="false" PhysicalOp="Constant Scan" EstimatedTotalSubtreeCost="1.57E-07"> 
    <OutputList> 
    <ColumnReference Database="[ee]" Schema="[dbo]" Table="[source]" Column="x" /> 
    <ColumnReference Database="[ee]" Schema="[dbo]" Table="[other]" Column="y" /> 
    <ColumnReference Column="Expr1022" /> 
    </OutputList> 
    <ConstantScan /> 
</RelOp> 

source sおよびother oは決してタッチされません。これは実際の出力を生成しないため、Nested Loopsの入力がないため、実際のシークは実行されません。

パラメータをbに置き換えると、実際のJOINの操作が3つのテーブルすべてに対してより複雑なプランになります。

+0

これは、私の例のビューに関してCTEを使用するときの評価が異なることをどのように説明していますか?これはすべて非常に興味深いですが、私はそれがどう違うか、むしろ、私が望むように動作するようにするために必要なことを理解していません。 –

関連する問題