単一行のINSERT…SELECTは、個別のSELECTよりもはるかに遅い


18

1〜400の番号が付けられた400行の次のヒープテーブルがあるとします。

DROP TABLE IF EXISTS dbo.N;
GO
SELECT 
    SV.number
INTO dbo.N 
FROM master.dbo.spt_values AS SV
WHERE 
    SV.[type] = N'P'
    AND SV.number BETWEEN 1 AND 400;

および次の設定:

SET NOCOUNT ON;
SET STATISTICS IO, TIME OFF;
SET STATISTICS XML OFF;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

次のSELECT文は約6秒で完了します(demoplan):

DECLARE @n integer = 400;

SELECT
    c = COUNT_BIG(*) 
FROM dbo.N AS N
CROSS JOIN dbo.N AS N2
CROSS JOIN dbo.N AS N3
WHERE 
    N.number <= @n
    AND N2.number <= @n
    AND N3.number <= @n
OPTION
    (OPTIMIZE FOR (@n = 1));

注:@このOPTIMIZE FOR節は、さまざまな理由で発生する可能性のあるカーディナリティーの誤推定など、実際の問題の本質的な詳細をキャプチャした合理的なサイズの再現を作成するためのものです。

単一行の出力がテーブルに書き込まれるとき、19秒かかります(demoplan):

DECLARE @T table (c bigint NOT NULL);

DECLARE @n integer = 400;

INSERT @T
    (c)
SELECT
    c = COUNT_BIG(*) 
FROM dbo.N AS N
CROSS JOIN dbo.N AS N2
CROSS JOIN dbo.N AS N3
WHERE 
    N.number <= @n
    AND N2.number <= @n
    AND N3.number <= @n
OPTION
    (OPTIMIZE FOR (@n = 1));

実行計画は、1行の挿入を除いて同一に見えます。

余分な時間はすべてCPU使用率によって消費されるようです。

INSERT声明がなぜそんなに遅いのですか?

回答:


21

SQL Serverは、行レベルのロックを使用して、ループ結合の内側のヒープテーブルをスキャンすることを選択します。通常、フルスキャンではページレベルのロックが選択されますが、テーブルのサイズと述語の組み合わせは、ストレージエンジンが行ロックを選択することを意味します。

オプティマイザが予想するよりも多くの回数OPTIMIZE FOR、ヒープがスキャンされるという手段によって意図的に導入されたカーディナリティの推定値は、通常のようにスプールを導入しません。

この要因の組み合わせは、実行時に必要なロックの数にパフォーマンスが非常に敏感であることを意味します。

このSELECTステートメントは、コミットされていないデータを読み取る危険がなく、行外のデータがない場合に、行レベルの共有ロックをスキップできる(意図共有のページレベルのロックのみを取得する)最適化の恩恵を受けます。

INSERT...SELECTRIDロックの何百万人が、意図共有ページレベルロックと一緒に、撮影した第2の場合には、各二をリリースしているので、文は、この最適化の恩恵を受けていません。

膨大な量のロックアクティビティは、余分なCPUと経過時間の原因となります。

最も自然な回避策は、オプティマイザ(およびストレージエンジン)が適切なカーディナリティの見積もりを取得し、適切な選択を行えるようにすることです。

実際のユースケースでそれが実用的でない場合は、INSERTand SELECTステートメントを分離して、結果をSELECT変数に保持することができます。これにより、SELECTステートメントがロックスキップの最適化の恩恵を受けることができます。

分離レベルの変更は、共有ロックを取得しないか、ロックのエスカレーションが迅速に行われるようにすることにより、機能させることもできます。

最後の関心SELECT事項として、文書化されていないトレースフラグ8691を使用してスプールの使用を強制することにより、最適化された場合よりもさらに高速にクエリを実行できます。

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