クラスタ化インデックス付きのテーブルへの効率的なINSERT


28

TRACKING_NUMBER列にクラスター化インデックスが設定されたテーブルに行を挿入するSQLステートメントがあります。

例えば:

INSERT INTO TABL_NAME (TRACKING_NUMBER, COLB, COLC) 
SELECT TRACKING_NUMBER, COL_B, COL_C 
FROM STAGING_TABLE

私の質問は-クラスター化インデックス列のSELECTステートメントでORDER BY句を使用するのに役立ちますか、またはORDER BY句に必要な追加の並べ替えによってゲインが無効になりますか?

回答:


18

他の回答がすでに示しているように、SQL Serverは、行がinsert

これは、プラン内のクラスター化インデックス演算子にDMLRequestSortプロパティセットがあるかどうかに依存します(プロパティセットは、挿入される行の推定数に依存します)。

何らかの理由でSQL Serverがこれを過小評価していることがわかった場合はORDER BYSELECTクエリに明示的に追加してページ分割を最小限に抑え、INSERT操作による断片化を回避することでメリットが得られる場合があります

例:

use tempdb;

GO

CREATE TABLE T(N INT PRIMARY KEY,Filler char(2000))

CREATE TABLE T2(N INT PRIMARY KEY,Filler char(2000))

GO

DECLARE @T TABLE (U UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),N int)

INSERT INTO @T(N)
SELECT number 
FROM master..spt_values
WHERE type = 'P' AND number BETWEEN 0 AND 499

/*Estimated row count wrong as inserting from table variable*/
INSERT INTO T(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2

/*Same operation using explicit sort*/    
INSERT INTO T2(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2
ORDER BY T1.N*1000 + T2.N


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T'), NULL, NULL, 'DETAILED')
;  


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T2'), NULL, NULL, 'DETAILED')
;  

T非常に断片化されているショー

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
99.3116118225536             92535                92535                67.1668272794663               250000
99.5                         200                  200                  74.2868173956017               92535
0                            1                    1                    32.0978502594514               200

しかし、T2断片化は最小限です

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
0.376                        262                  62500                99.456387447492                250000
2.1551724137931              232                  232                  43.2438349394613               62500
0                            1                    1                    37.2374598468001               232

逆に、データが既に事前に並べ替えられており、不要な並べ替えを避けたい場合は、SQL Serverに強制的に行数を過小評価させたい場合があります。注目すべき例の1つは、newsequentialidクラスター化インデックスキーを持つテーブルに多数の行を挿入する場合です。Denaliより前のバージョンのSQL Serverでは、SQL Serverは不要で潜在的に高価なソート操作を追加します。これは次の方法で回避できます

DECLARE @var INT =2147483647

INSERT INTO Foo
SELECT TOP (@var) *
FROM Bar

その後、SQL Serverは、サイズがBarプランにソートが追加されるしきい値を下回っていても、100行が挿入されると推定します。ただし、以下のコメントで指摘されているように、これは挿入が残念ながら最小限のロギングを利用できないことを意味します。



12

オプティマイザーが挿入前にデータをソートする方が効率的であると判断した場合、挿入演算子の上流のどこかでソートします。クエリの一部として並べ替えを導入する場合、オプティマイザーはデータが既に並べ替えられていることを認識し、再度並べ替えを省略する必要があります。選択した実行計画は、ステージングテーブルから挿入された行の数に応じて実行ごとに異なる場合があります。

明示的なソートの有無にかかわらずプロセスの実行計画をキャプチャできる場合は、コメントのために質問に添付してください。

編集:2011-10-28 17:00

@Gonsaluの答えは、ソート操作が常に発生することを示しているように見えますが、そうではありません。デモスクリプトが必要です!

スクリプトがかなり大きくなってきたので、私はそれらをGistに移動しました。実験を容易にするために、スクリプトはSQLCMDモードを使用します。テストは、2K5SP3、デュアルコア、8GBで実行されます。

挿入テストは3つのシナリオをカバーします。

  1. ターゲットと同じ順序でステージングデータのクラスター化インデックス。
  2. クラスター化インデックスのステージングデータの逆順。
  3. ランダムなINTを含むcol2によってクラスター化されたステージングデータ。

最初の実行、25行の挿入。

1回目の実行、25行

3つの実行計画はすべて同じであり、計画内のどこにも並べ替えは行われず、クラスター化インデックススキャンは "ordered = false"です。

2行目、26行の挿入。

2回目、26行

今回は計画が異なります。

  • 最初の例は、ordered = falseとしてクラスター化インデックススキャンを示しています。ソースデータが適切にソートされているため、ソートは発生していません。
  • 2番目では、ordered = trueとしてクラスター化インデックスを逆方向にスキャンします。したがって、ソート操作はありませんが、データをソートする必要があることはオプティマイザーによって認識され、逆順でスキャンします。
  • 3番目はソート演算子を示しています。

そのため、オプティマイザーがソートが必要であると判断する転換点があります。@MartinSmithが示すように、これは挿入される推定行に基づいているようです。私のテストリグでは、25はソートを必要としませんが、26は必要です(2K5SP3、デュアルコア、8GB)

SQLCMDスクリプトには、テーブル内の行のサイズを変更できる(ページ密度を変更する)変数と、追加の挿入前のdbo.MyTableの行数を含む変数が含まれています。私のテストから、どちらも転換点に影響を与えません。

読者がそんなに傾いている場合は、スクリプト実行し、コメントとして転換点を追加してください。テスト装置やバージョンによって異なるかどうか聞いてみたい。

編集:2011-10-28 20:15

同じリグで2K8R2を使用してテストを繰り返しました。今回は、転換点は251行です。繰り返しますが、ページ密度と既存の行数を変更しても効果はありません。


8

ステートメントORDER BY内の句SELECTは冗長です。

ソートされる必要がある場合、挿入される行はとにかくソートされるため、冗長です。

テストケースを作成しましょう。

CREATE TABLE #Test (
    id INTEGER NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX CL_Test_ID ON #Test (id);

CREATE TABLE #Sequence (
    number INTEGER NOT NULL
);

INSERT INTO #Sequence
SELECT number FROM master..spt_values WHERE name IS NULL;

実際のクエリプランのテキスト表示を有効にすると、クエリプロセッサによって実行されるタスクを確認できます。

SET STATISTICS PROFILE ON;
GO

ここでINSERTORDER BY句なしで2K行を表に入れましょう。

INSERT INTO #Test
SELECT number
  FROM #Sequence

このクエリの実際の実行計画は次のとおりです。

INSERT INTO #Test  SELECT number    FROM #Sequence
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

ご覧のとおり、実際のINSERTが発生する前にソート演算子があります。

次に、テーブルをクリアINSERTし、ORDER BY句を使用して2k行をテーブルに追加します。

TRUNCATE TABLE #Test;
GO

INSERT INTO #Test
SELECT number
  FROM #Sequence
 ORDER BY number

このクエリの実際の実行計画は次のとおりです。

INSERT INTO #Test  SELECT number    FROM #Sequence   ORDER BY number
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

句のINSERTないステートメントに使用されたのと同じ実行プランであることに注意してくださいORDER BY

現在、マークスミスが別の回答で示しているように(挿入する行の数が少ない場合)、Sort操作は必ずしも必要ではありませんが、明示的なであっても操作は生成されないため、その場合は句はまだ冗長ですクエリプロセッサによって。ORDER BYORDER BYSort

INSERTminimally-loggedを使用して、クラスター化インデックスを使用してステートメントをテーブルに最適化できますがINSERT、これはこの質問の範囲外です。

2011年11月2日更新: Mark Smithが示したようにINSERTクラスター化インデックスのあるテーブルへのsは常にソートする必要があるとは限りませんがORDER BY、その場合も句は冗長です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.