Warum ich beim zweiten Versuch, dieselbe Zeile einzufügen, die bereits eingefügt wurde, zu einem Fehler geführt hat. Wenn diese Zeile die maximale Zeilengröße überschreitet, ist zu erwarten, dass sie überhaupt nicht eingefügt werden kann.
Zunächst vielen Dank für das Reproduktionsskript.
Das Problem ist nicht, dass SQL Server eine bestimmte vom Benutzer sichtbare Zeile nicht einfügen oder aktualisieren kann . Wie Sie bereits bemerkt haben, kann eine Zeile, die bereits in eine Tabelle eingefügt wurde, für SQL Server nicht grundsätzlich zu groß sein.
Das Problem tritt auf, weil die SQL Server- MERGE
Implementierung berechnete Informationen (als zusätzliche Spalten) während Zwischenschritten im Ausführungsplan hinzufügt. Diese zusätzlichen Informationen werden aus technischen Gründen benötigt, um zu verfolgen, ob jede Zeile zum Einfügen, Aktualisieren oder Löschen führen soll. und auch im Zusammenhang mit der Art und Weise, wie SQL Server vorübergehende Schlüsselverletzungen bei Änderungen an Indizes generell vermeidet.
Die SQL Server Storage Engine erfordert, dass Indizes zu jeder Zeit - intern, einschließlich jeder versteckten Eindeutigkeit) eindeutig sind - während jede Zeile verarbeitet wird - und nicht zu Beginn und am Ende der vollständigen Transaktion. In komplexeren MERGE
Szenarien erfordert dies eine Aufteilung (Konvertieren eines Updates in ein separates Löschen und Einfügen), Sortieren und ein optionales Reduzieren (Verwandeln benachbarter Einfügungen und Aktualisierungen auf demselben Schlüssel in ein Update). Weitere Informationen .
Beachten Sie außerdem, dass das Problem nicht auftritt, wenn die Zieltabelle ein Heap ist (löschen Sie den Clustered-Index, um dies anzuzeigen). Ich empfehle dies nicht als Fix, sondern erwähne es nur, um den Zusammenhang zwischen der jederzeitigen Beibehaltung der Index-Eindeutigkeit (im vorliegenden Fall gruppiert) und dem Split-Sort-Collapse hervorzuheben.
Bei einfachen MERGE
Abfragen mit geeigneten eindeutigen Indizes und einer einfachen Beziehung zwischen Quell- und Zielzeilen (die normalerweise mit einer ON
Klausel übereinstimmen , die alle Schlüsselspalten enthält) kann der Abfrageoptimierer einen Großteil der generischen Logik vereinfachen, was zu vergleichsweise einfachen Plänen führt Sie benötigen kein Split-Sort-Collapse- oder Segment-Sequence-Projekt, um zu überprüfen, ob die Zielzeilen nur einmal berührt werden.
Bei komplexen MERGE
Abfragen mit undurchsichtigerer Logik kann der Optimierer diese Vereinfachungen normalerweise nicht anwenden, wodurch viel mehr der grundlegend komplexen Logik verfügbar gemacht wird, die für die korrekte Verarbeitung erforderlich ist (trotz Produktfehlern, und es gab viele ).
Ihre Anfrage gilt sicherlich als komplex. Die ON
Klausel stimmt nicht mit den Indexschlüsseln überein (und ich verstehe warum), und die 'Quellentabelle' ist eine Selbstverknüpfung, die eine Funktion des Ranking-Fensters beinhaltet (wiederum mit Gründen):
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])
...
Dies führt zu vielen zusätzlichen berechneten Spalten, die hauptsächlich dem Split und den Daten zugeordnet sind, die benötigt werden, wenn ein Update in ein Einfüge- / Update-Paar konvertiert wird. Diese zusätzlichen Spalten führen dazu, dass eine Zwischenzeile die zulässigen 8060 Bytes bei einer früheren Sortierung überschreitet - die unmittelbar nach einem Filter:
Beachten Sie, dass der Filter 1.319 Spalten (Ausdrücke und Basisspalten) in seiner Ausgabeliste enthält. Das Anhängen eines Debuggers zeigt den Aufrufstapel an dem Punkt an, an dem die schwerwiegende Ausnahme ausgelöst wird:
Hinweis beiläufig , dass das Problem nicht an der Spool - die Ausnahme ist auf eine Warnung über das umgewandelte Potenzial für eine Reihe zu groß zu sein.
Warum ist die Aktualisierung mithilfe der Zusammenführung nicht erfolgreich, während die Einfügung und die direkte Aktualisierung ebenfalls erfolgreich ist?
Ein direktes Update hat nicht die gleiche interne Komplexität wie das MERGE
. Es ist eine grundlegend einfachere Operation, die dazu neigt, besser zu vereinfachen und zu optimieren. Durch das Entfernen der NOT MATCHED
Klausel wird möglicherweise auch genug Komplexität entfernt, sodass der Fehler in einigen Fällen nicht generiert wird. Das passiert beim Repro allerdings nicht.
Letztendlich ist mein Rat, MERGE
größere oder komplexere Aufgaben zu vermeiden . Ich habe die Erfahrung gemacht, dass separate Anweisungen zum Einfügen / Aktualisieren / Löschen tendenziell besser optimieren, einfacher zu verstehen sind und im Vergleich zu Anweisungen häufig insgesamt eine bessere Leistung erbringen MERGE
.