すでに挿入されている同じ行を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
ます。