SQL Serverのマージ行サイズオーバーフロー-「サイズの行を作成できません。」


8

データをマージしようとしているターゲットテーブルには、約660列があります。マージのコード:

MERGE TBL_BM_HSD_SUBJECT_AN_1 AS targetTable
USING
        (                
SELECT * 
FROM TBL_BM_HSD_SUBJECT_AN_1_STAGING
WHERE [ibi_bulk_id] in (20150520141627106) and  id in(101659113)
    ) AS sourceTable 
ON (...)
WHEN MATCHED AND ((targetTable.[sampletime] <= sourceTable.[sampletime]))
        THEN UPDATE SET ...
WHEN NOT MATCHED 
        THEN INSERT (...)
    VALUES (...)

初めてこれを実行したとき(つまり、テーブルが空のとき)は成功し、1行挿入しました。

同じデータセットを使用してこれを2回実行したときに、エラーが返されました。
許容最大行サイズ8060より大きいサイズ8410の行を作成できません。

すでに挿入されている同じ行を2回目にマージしようとしたときにエラーが発生したのはなぜですか。この行が最大行サイズを超えた場合、そもそも挿入できない可能性があります。

だから私は2つのことを試しました(そして成功しました!):

  • マージステートメントから「WHEN NOT MATCHED」セクションを削除する
  • マージしようとした同じ行で更新ステートメントを実行する

挿入を使用してマージを使用した更新が成功せず、直接更新も成功するのはなぜですか?

更新:

実際の行サイズ-4978を見つけることができました。この行のみを含む新しいテーブルを作成し、次の方法で行サイズを見つけました。 ここに画像の説明を入力してください

それでも、許容範囲を超えるものはまだ表示されません。

更新(2):

完全再現

これを再現するために追加の補助オブジェクトが必要ないこと、およびデータが(ある程度)難読化されるように努力した。

バージョン2012からの1つと2008年からの1つをいくつかのサーバーで試してみたところ、すべてのサーバーで完全に再現することができました。

回答:


10

すでに挿入されている同じ行を2回目にマージしようとしたときにエラーが発生したのはなぜですか。この行が最大行サイズを超えた場合、そもそも挿入できない可能性があります。

まず、再現台本ありがとうございます。

問題は、SQL Serverが特定のユーザーに表示される 行を挿入または更新できないことではありません。お気づきのように、すでにテーブルに挿入されている行は、SQL Serverが処理するには根本的に大きすぎてはなりません。

この問題は、SQL ServerのMERGE実装が実行計画の中間ステップで計算された情報を(追加の列として)追加するために発生します。この追加情報は、技術的な理由で、各行が挿入、更新、または削除になるかどうかを追跡するために必要です。また、SQL Serverがインデックスの変更中に一時的なキー違反を一般的に回避する方法にも関連しています。

SQL Serverストレージエンジンでは、完全なトランザクションの開始時と終了時ではなく、各行が処理されるときに、インデックスが常に(非表示の一意識別子を含む内部で)一意である必要があります。より複雑なMERGEシナリオでは、これには分割(更新を個別の削除と挿入に変換)、並べ替え、およびオプションの折りたたみ(同じキーの隣接する挿入と更新を更新に変換する)が必要です。さらなる情報

余談ですが、ターゲットテーブルがヒープの場合は問題が発生しないことに注意してください(これを確認するには、クラスター化インデックスを削除します)。これを修正としてお勧めするのではなく、常にインデックスの一意性を維持する(この場合はクラスター化)とSplit-Sort-Collapseの間の関係を強調するために言及するだけです。

では、簡単な MERGE適した問合せ、ユニークインデックス、および直接的な関係(一般的に使用してマッチングソースとターゲット行の間のONすべてのキー列を備え句)を、クエリオプティマイザは、比較的簡単な計画があることを行うには、その結果、離れて一般的なロジックの多くを簡素化することができますターゲット行が一度だけタッチされていることを確認するために、Split-Sort-CollapseまたはSegment-Sequenceプロジェクトは必要ありません。

では、複雑な MERGEクエリを、より不透明なロジックと、オプティマイザは、はるかに正確な処理のために必要な基本的に複雑なロジックの露出、通常、これらの単純化を適用することができない(にもかかわらず、製品のバグ、そしてたくさんありました)。

あなたのクエリは確かに複雑とみなされます。ON句は、インデックスキーと一致していない(と私は理由を理解)、および「ソーステーブルは、」自己結合(上の理由で再び、)順位ウィンドウ関数を含む次のとおりです。

MERGE MERGE_REPRO_TARGET AS targetTable
USING
(
    SELECT * FROM 
    (
        SELECT 
            *, 
            ROW_NUMBER() OVER (
                PARTITION BY ww,id, tenant 
                ORDER BY 
                (
                    SELECT COUNT(1) 
                    FROM MERGE_REPRO_SOURCE AS targetTable
                    WHERE 
                        targetTable.[ibi_bulk_id] = sourceTable.[ibi_bulk_id] 
                        AND targetTable.[ibi_row_id] <> sourceTable.[ibi_row_id] 
                        AND 
                        (
                            (targetTable.[ww] = sourceTable.[ww]) 
                            AND (targetTable.[id] = sourceTable.[id]) 
                            AND (targetTable.[tenant] = sourceTable.[tenant])
                        ) 
                        AND NOT ((targetTable.[sampletime] <= sourceTable.[sampletime]))
                ),
                sourceTable.ibi_row_id DESC
            ) AS idx
        FROM MERGE_REPRO_SOURCE sourceTable 
        WHERE [ibi_bulk_id] in (20150803110418887)
    ) AS bulkData
    where idx = 1
) AS sourceTable 
ON 
    (targetTable.[ww] = sourceTable.[ww]) 
    AND (targetTable.[id] = sourceTable.[id]) 
    AND (targetTable.[tenant] = sourceTable.[tenant])
...

これにより、主にSplitに関連付けられた多くの追加の計算された列と、更新が挿入/更新のペアに変換されるときに必要なデータが生成されます。これらの追加の列により、以前のソートで許可された8060バイトを超える中間行が生成されます-フィルターの直後の行:

問題の並べ替え

フィルターの出力リストには1,319列(式と基本列)があることに注意してください。デバッガーをアタッチすると、致命的な例外が発生した時点で呼び出しスタックが表示されます。

スタックトレース

ちなみに問題はスプールにあるのではないことに注意してください。例外は、行が大きすぎる可能性があるという警告に変換されます。

挿入を使用してマージを使用した更新が成功せず、直接更新も成功するのはなぜですか?

直接更新の内部の複雑さはと同じではありませんMERGE。これは基本的にシンプルな操作であり、シンプル化とオプティマイザの改善に役立ちます。削除するNOT MATCHED句もエラーがいくつかのケースで生成されていないような複雑さを十分に取り除いてもよいです。ただし、これは再現では発生しません。

結局のところ、私のアドバイスはMERGE、より大きなまたはより複雑なタスクを避けることです。私の経験では、個別の挿入/更新/削除ステートメントは最適化する傾向があり、理解するのが簡単であり、全体と比較してパフォーマンスがよくなることよくありMERGEます。

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