web-dev-qa-db-ja.com

SQL ServerクエリプランのCPUサイクルを節約します(ワイドテーブルの場合よりも統計なしの方が優れています)

問題のクエリに非常に幅広い列のセットがあると仮定します。クエリは次のようになります。

set statistics io,time on;

Select a.Col1, a.Col2, b.Col3, b.Col4, c.Col5
From FirstWideTable a
   Join SecondWideTable b on a.Col6=b.Col7
   Join ThirdWideTable c on b.Col8=c.Col9
WHERE a.Col10=@SomeVariable;

set statistics io,time off;

上記のクエリでは、100列程度の10列を使用しています(より「選択的な」クエリ)。 SQL Serverが作成したこれらのテーブルとテーブルの統計には、述語(Col10)に対応する有効なインデックスが含まれていますが、ここには表示されていませんが、3つのテーブルにPKが作成されていると想定しています(下のデモを参照)。

CREATE NONCLUSTERED INDEX NC__a_Col1_Col2_Col6 
ON FirstWideTable(Col1 ASC,Col2 ASC, Col3 ASC) 
INCLUDE (Col10) 
ON PRIMARY;

セカンドおよびサードワイドテーブルでは、同様のインデックスを使用できます。

クエリが必要な10列のみのサブクエリとして記述されている場合:

set statistics io,time on;

Select a.Col1, a.Col2, b.Col3, b.Col4, c.Col5
From (SELECT a.Col1, a.Col2,a.Col6,a.Col10 FROM FirstWideTable) a
    Join (SELECT b.Col4,b.Col5, b.Col7 FROM SecondWideTable) b on a.Col6=b.Col7
    Join (c.Col5, c.Col9 FROM ThirdWideTable) c on b.Col8=c.Col9
WHERE a.Col10=@SomeVariable;

set statistics io,time off;

結果は改善します。統計のある幅の広いテーブルの経過時間は、ひどい(30秒)経過時間を示しています。クエリ(統計に対して実行されていない統計)の実際の列がサブクエリにリストされるたびに、経過時間が大幅に減少します。

クエリ結果-ワイドテーブルには統計があります-ワイドテーブルがベースラインです。サブクエリの結果には統計がありません。タイミングは、ワイドテーブルではなく必要な列のみを調べるサブクエリを追加した後に取得されました。

                                      CPU Time (ms) Elapsed Time (ms)
Wide table result                           7265    35459
Subqueried narrow result table 1            5125    31271
Wide table result                           6765    33446
Subqueried narrow result table 2            5391    27099
Wide table result                           7203    34354
Subqueried narrow result table 3            5843    10321

以下の理論データからの結果:

                                      CPU Time (ms) Elapsed Time (ms)
Wide table result                           109     461
Subqueried narrow result table 1            32      441
Wide table result                           78      441
Subqueried narrow result table 2            110     453
Wide table result                           62      441
Subqueried narrow result table 3            63      458

これが理論的なデータがどのように生成されたかです。/*ダミーのソーステーブルを作成します* /

select * into tblSourceTable from(select top 100000 NewID()A, NewID()B from sys.columns)x

理論的なデータを生成します。

DECLARE @test2  TABLE (
    [ID] [bigint] NULL,
    [A] [uniqueidentifier] NULL,
    [B] [uniqueidentifier] NULL,
    [C] [uniqueidentifier] NULL,
    [D] [uniqueidentifier] NULL,
    [E] [uniqueidentifier] NULL,
    [F] [uniqueidentifier] NULL,
    [G] [uniqueidentifier] NULL,
    [H] [uniqueidentifier] NULL,
    [I] [uniqueidentifier] NULL,
    [J] [uniqueidentifier] NULL,
    [K] [uniqueidentifier] NULL,
    [L] [uniqueidentifier] NULL,
    [M] [uniqueidentifier] NULL,
    [N] [uniqueidentifier] NULL,
    [O] [uniqueidentifier] NULL,
    [P] [uniqueidentifier] NULL,
    [Q] [uniqueidentifier] NULL,
    [R] [uniqueidentifier] NULL,
    [S] [uniqueidentifier] NULL,
    [T] [uniqueidentifier] NULL,
    [U] [uniqueidentifier] NULL,
    [V] [uniqueidentifier] NULL,
    [W] [uniqueidentifier] NULL,
    [X] [uniqueidentifier] NULL,
    [Y] [uniqueidentifier] NULL,
    [Z] [uniqueidentifier] NULL,
    [AA] [uniqueidentifier] NULL,
    [AB] [uniqueidentifier] NULL,
    [AC] [uniqueidentifier] NULL,
    [AD] [uniqueidentifier] NULL,
    [AE] [uniqueidentifier] NULL,
    [AF] [uniqueidentifier] NULL,
    [AG] [uniqueidentifier] NULL,
    [AH] [uniqueidentifier] NULL,
    [AI] [uniqueidentifier] NULL,
    [AJ] [uniqueidentifier] NULL,
    [AK] [uniqueidentifier] NULL,
    [AL] [uniqueidentifier] NULL,
    [AM] [uniqueidentifier] NULL,
    [AN] [uniqueidentifier] NULL,
    [AO] [uniqueidentifier] NULL,
    [AP] [uniqueidentifier] NULL,
    [AQ] [uniqueidentifier] NULL,
    [AR] [uniqueidentifier] NULL
) 

ダミーデータセットを作成する

insert into @test2
select Top 50000 ID=ROW_NUMBER()over(order by C.A),
NewID() AS A,NewID() AS B,NewID() AS C,NewID() AS D,NewID() AS E,NewID() AS F, 
NewID() AS G,NewID() AS H,NewID() AS I,NewID() AS J,NewID() AS K,NewID() AS L, 
NewID() AS M,NewID() AS N,NewID() AS O,NewID() AS P,NewID() AS Q,NewID() AS R, 
NewID() as S,NewID() AS T,NewID() AS U,NewID() AS V,NewID() AS W,NewID() AS X, 
NewID() AS Y,NewID() AS Z,NewID() AS AA,NewID() AS AB,NewID() AS AC,NewID() AS AD, 
NewID() AS AE,NewID() AS AF,NewID() AS AG,NewID() AS AH,NewID() AS AI,NewID() AS AJ, 
NewID() AS AK,NewID() AS AL,NewID() AS AM,NewID() AS AN,NewID() AS AO,NewID() AS AP, 
NewID() AS AQ,NewID() AS AR
FROM test C
cross apply test D
cross apply test E
cross apply test F
cross apply test G
cross apply test H
cross apply test I
cross apply test J
cross apply test K
cross apply test L
cross apply test M
cross apply test N
cross apply test O
cross apply test P
cross apply test Q
cross apply test R
cross apply test S
cross apply test T
cross apply test U
cross apply test V
cross apply test W
cross apply test X
cross apply test Y
cross apply test Z

テーブルを作成します。

Select * into [FirstWideTable] FROM @test2
Select * into [SecondWideTable] FROM @test2
Select * into [ThirdWideTable] FROM @test2
CREATE UNIQUE CLUSTERED INDEX [PK_TEST_A_ID] ON [dbo].[FirstWideTable]([ID] ASC)
CREATE UNIQUE CLUSTERED INDEX [PK_TEST_B_ID] ON [dbo].[SecondWideTable]([ID] ASC)
CREATE UNIQUE CLUSTERED INDEX [PK_TEST_C_ID] ON [dbo].[ThirdWideTable]([ID] ASC)

クエリを数回実行して、統計データを作成できるようにします

Select  a.ID, a.A, a.AB, b.A, b.B, b.C, b.D, c.A, C.B, c.D, c.AA
From [FirstWideTable] a
   LEFT Join [SecondWideTable] b on a.ID=b.ID
   LEFT Join [ThirdWideTable] c on b.ID=c.ID
WHERE a.ID BETWEEN 1 and 50000;

Select  a.ID, a.A, a.AB, b.A, b.B, b.C, b.D, c.A, C.B, c.D, c.AA
From [FirstWideTable] a
   LEFT Join [SecondWideTable] b on a.ID=b.ID
   LEFT Join [ThirdWideTable] c on b.ID=c.ID
WHERE a.ID BETWEEN 1 and 50000;

Select  a.ID, a.A, a.AB, b.A, b.B, b.C, b.D, c.A, C.B, c.D, c.AA
From [FirstWideTable] a
   LEFT Join [SecondWideTable] b on a.ID=b.ID
   LEFT Join [ThirdWideTable] c on b.ID=c.ID
WHERE a.ID BETWEEN 1 and 50000;

統計を作成する

update statistics [FirstWideTable]
update statistics [SecondWideTable]
update statistics [ThirdWideTable]

結果をつかむ

set statistics io,time on
Select a.ID, a.A, a.AB, b.A, b.B, b.C, b.D, c.A, C.B, c.D, c.AA
From (Select ID, A, AB from [FirstWideTable]) a
   LEFT Join(Select ID, A, B, C, D from[SecondWideTable]) b on a.ID=b.ID
   LEFT Join(Select ID, A, B, D, AA from[ThirdWideTable]) c on b.ID=c.ID
WHERE a.ID BETWEEN 1 and 50000;
set statistics io,time off

set statistics io,time on
Select  a.ID, a.A, a.AB, b.A, b.B, b.C, b.D, c.A, C.B, c.D, c.AA
From [FirstWideTable] a
   LEFT Join [SecondWideTable] b on a.ID=b.ID
   LEFT Join [ThirdWideTable] c on b.ID=c.ID
WHERE a.ID BETWEEN 1 and 50000;
set statistics time io,off 
3
Jamie

特に統計が古くなっている場合は、経過時間の改善が必ずしも見られない場合でも、CPU時間は計画間で大幅に異なる場合があります。 CPU時間と経過時間の関係を説明する短い回答 here を書きました。

UPDATE STATISTICS 影響を受けるテーブルのT-SQLコマンド。

2
Max Vernon