2

以下は、セットアップとテストのスクリプトです。テスト スクリプトは、テーブル T4 のみをスキャンすることを想定しています。ただし、10000 行を超えると、テーブル T1 と T4 の両方のスキャンが開始されます。

create table T1 (A varchar(5) check  ((A='S4' or A='S3' or A='S2' or A='S1' or A='FS' or A='FM' or A='FBL' or A='ES' or A='EBL' or A='BL'))
                ,DateX date
                ,id char(6)
                ,DateY date
                ,primary key clustered (A, DateX, id))
create table T4 (A varchar(5) check ((A='S1780' OR A='C1780' OR A='B1780'))
                ,DateX date
                ,id char(6)
                ,DateY date
                ,primary key clustered (A, DateX, id));
-- Insert some values
go
create view dbo.tall
as
select * from    dbo.T1
union all
select * from    dbo.T4

テストコード:

declare @A table (A varchar(5) primary key (A));
insert  @A
values  ('S1780'), ('C1780'), ('B1780');

with    a as (select    *
              from      tall
              where     A in (select    *
                              from      @A)
             ),
        sd
          as (select    A, max(DateY) DateY
              from      a
              group by  A
             ),
        filter24m 
        -- Un-comment the lines in this CTE will make the scanning T1 occur with even less row count
          as (select    id, a.A --, sd.DateY
              from      a
                        join sd on a.A = sd.A
              --where     DateX between dateadd(mm, 1, sd.DateY) and dateadd(mm, 24 + 1, sd.DateY) --
              --group by  id, a.A, sd.DateY
              --having    count(*) = 24
             )
    --
             select *
             from   filter24m

不適切な実行 (T1 に 100 行、T4 に 10000 行がある場合のテスト):

テーブル「T4」。スキャン カウント 2、論理読み取り 80、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
テーブル「#1B3A42B1」。スキャン カウント 1、論理読み取り 6、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
テーブル「ワークテーブル」。スキャン カウント 0、論理読み取り 0、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
テーブル「ワークテーブル」。スキャン カウント 0、論理読み取り 0、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
テーブル「T1」。スキャン カウント 3、論理読み取り 6、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
  |--Concatenation
       |--Nested Loops(Inner Join, OUTER REFERENCES:([workdb].[dbo].[T1].[A]))
       |    |--Nested Loops(Inner Join, OUTER REFERENCES:([A]))
       |    |    |--Clustered Index Seek(OBJECT:(@A), SEEK:([A] >= 'B1780' AND [A] <= 'S4') ORDERED FORWARD)
       |    |    |--Stream Aggregate(DEFINE:([workdb].[dbo].[T1].[A]=ANY([workdb].[dbo].[T1].[A])))
       |    |         |--Nested Loops(Inner Join, OUTER REFERENCES:([workdb].[dbo].[T1].[A]))
       |    |              |--Stream Aggregate(DEFINE:([workdb].[dbo].[T1].[A]=ANY([workdb].[dbo].[T1].[A])))
       |    |              |    |--Clustered Index Seek(OBJECT:([workdb].[dbo].[T1].[PK__T1__EE1DD21123AC6823]), SEEK:([workdb].[dbo].[T1].[A]=[A]) ORDERED FORWARD)
       |    |              |--Clustered Index Seek(OBJECT:(@A), SEEK:([A]=[workdb].[dbo].[T1].[A]),  WHERE:([A]>='B1780' AND [A]<='S4') ORDERED FORWARD)
       |    |--Clustered Index Seek(OBJECT:([workdb].[dbo].[T1].[PK__T1__EE1DD21123AC6823]), SEEK:([workdb].[dbo].[T1].[A]=[workdb].[dbo].[T1].[A]) ORDERED FORWARD)
       |--Merge Join(Inner Join, MERGE:([workdb].[dbo].[T4].[A])=([workdb].[dbo].[T4].[A]), RESIDUAL:([workdb].[dbo].[T4].[A]=[workdb].[dbo].[T4].[A]))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([workdb].[dbo].[T4].[A]))
            |    |--Nested Loops(Inner Join, OUTER REFERENCES:([workdb].[dbo].[T4].[A]))
            |    |    |--Stream Aggregate(GROUP BY:([workdb].[dbo].[T4].[A]))
            |    |    |    |--Clustered Index Scan(OBJECT:([workdb].[dbo].[T4].[PK__T4__EE1DD21128711D40]), ORDERED FORWARD)
            |    |    |--Clustered Index Seek(OBJECT:(@A), SEEK:([A]=[workdb].[dbo].[T4].[A]),  WHERE:([A]>='B1780' AND [A]<='S4') ORDERED FORWARD)
            |    |--Clustered Index Seek(OBJECT:(@A), SEEK:([A]=[workdb].[dbo].[T4].[A]),  WHERE:([A]>='B1780' AND [A]<='S4') ORDERED FORWARD)
            |--Clustered Index Scan(OBJECT:([workdb].[dbo].[T4].[PK__T4__EE1DD21128711D40]), ORDERED FORWARD)

良い (両方のテーブルに 100 行しかない場合のテスト):

テーブル「#1DE1A532」。スキャン カウント 101、論理読み取り 202、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
テーブル「ワークテーブル」。スキャン カウント 0、論理読み取り 0、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
テーブル「T4」。スキャン カウント 103、論理読み取り 206、物理読み取り 0、先読み読み取り 0、LOB 論理読み取り 0、LOB 物理読み取り 0、LOB 先読み読み取り 0。
  |--Nested Loops(Inner Join, OUTER REFERENCES:([Union1006]))
       |--Nested Loops(Inner Join, OUTER REFERENCES:([A]))
       |    |--Clustered Index Seek(OBJECT:(@A), SEEK:([A] >= 'B1780' AND [A] <= 'S4') ORDERED FORWARD)
       |    |--Concatenation
       |         |--Filter(WHERE:(STARTUP EXPR([A]='BL' OR [A]='EBL' OR [A]='ES' OR [A]='FBL' OR [A]='FM' OR [A]='FS' OR [A]='S1' OR [A]='S2' OR [A]='S3' OR [A]='S4')))
       |         |    |--Clustered Index Seek(OBJECT:([workdb].[dbo].[T1].[PK__T1__EE1DD21123AC6823]), SEEK:([workdb].[dbo].[T1].[A]=[A]) ORDERED FORWARD)
       |         |--Filter(WHERE:(STARTUP EXPR([A]='B1780' OR [A]='C1780' OR [A]='S1780')))
       |              |--Clustered Index Seek(OBJECT:([workdb].[dbo].[T4].[PK__T4__EE1DD21128711D40]), SEEK:([workdb].[dbo].[T4].[A]=[A]) ORDERED FORWARD)
       |--Top(TOP EXPRESSION:((1)))
            |--Nested Loops(Inner Join, WHERE:([Union1019]=[A]))
                 |--Concatenation
                 |    |--Filter(WHERE:(STARTUP EXPR([Union1006]='B1780' OR [Union1006]='C1780' OR [Union1006]='S1780')))
                 |    |    |--Clustered Index Seek(OBJECT:([workdb].[dbo].[T4].[PK__T4__EE1DD21128711D40]), SEEK:([workdb].[dbo].[T4].[A]=[Union1006]) ORDERED FORWARD)
                 |    |--Filter(WHERE:(STARTUP EXPR([Union1006]='BL' OR [Union1006]='EBL' OR [Union1006]='ES' OR [Union1006]='FBL' OR [Union1006]='FM' OR [Union1006]='FS' OR [Union1006]='S1' OR [Union1006]='S2' OR [Union1006]='S3' OR [Union1006]='S4')))
                 |         |--Clustered Index Seek(OBJECT:([workdb].[dbo].[T1].[PK__T1__EE1DD21123AC6823]), SEEK:([workdb].[dbo].[T1].[A]=[Union1006]) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:(@A), SEEK:([A] >= 'B1780' AND [A] <= 'S4') ORDERED FORWARD)

xml での適切な実行計画:

https://docs.google.com/file/d/0B6OXmuJYfpRcTE9Pd0xpSEhEQy04eWZqa2lKejM5YkdPRHFr/edit?usp=docslist_api

xml の不適切な実行計画: https://docs.google.com/file/d/0B6OXmuJYfpRcU2ZUVFdtLUcxQk83TVFSNUFoZEYtbVdaWU4w/edit?usp=docslist_api

4

1 に答える 1

0

ビューが分割されているため、T1 がスキャンされないことが予想されます。ただし、テーブル変数が原因でスキャンが発生しています。テーブル変数から選択するステートメントは、値を作成してテーブル変数に挿入するステートメントとは別に解析されるため、オプティマイザーは値を認識しません。

(SELECT * FROM @a) ではなくリテラル値を使用すると、T1 は参照されず、単に T4 がスキャンされます。ただし、奇妙なことに、これはコストが高く、パフォーマンスが低下します。

パフォーマンスを最適化するために、含まれる列を使用して DateY にインデックスを作成することを検討してください。

于 2014-09-09T01:09:40.010 に答える