Почему второй раз, когда я попытался объединить ту же строку, которая уже была вставлена, это привело к ошибке. Если эта строка превысила максимальный размер строки, можно было бы ожидать, что ее невозможно будет вставить в первую очередь.
Во-первых, спасибо за сценарий воспроизведения.
Проблема не в том, что SQL Server не может вставить или обновить конкретную видимую пользователю строку. Как вы заметили, строка, уже вставленная в таблицу, безусловно, не может быть слишком большой для обработки SQL Server.
Проблема возникает из-за того, что реализация SQL Server MERGEдобавляет вычисленную информацию (в виде дополнительных столбцов) на промежуточных этапах плана выполнения. Эта дополнительная информация необходима по техническим причинам, чтобы отслеживать, должна ли каждая строка приводить к вставке, обновлению или удалению; а также связано с тем, как SQL Server обычно предотвращает временные нарушения ключей при изменениях индексов.
Механизм хранения SQL Server требует, чтобы индексы были уникальными (внутренне, включая любой скрытый Uniquifier) всегда - при обработке каждой строки - а не в начале и в конце полной транзакции. В более сложных MERGEсценариях это требует разделения (преобразование обновления в отдельное удаление и вставку), сортировки и необязательного свертывания (превращение смежных вставок и обновлений для одного и того же ключа в обновление). Более подробная информация .
Кроме того, обратите внимание, что проблема не возникает, если целевая таблица представляет собой кучу (удалите кластерный индекс, чтобы увидеть это). Я не рекомендую это как исправление, просто упомяну это, чтобы подчеркнуть связь между поддержанием уникальности индекса во все времена (в данном случае кластеризованным) и разделением-сортировкой-свертыванием.
В простых MERGE запросах с подходящими уникальными индексами и прямыми отношениями между исходными и целевыми строками (обычно совпадающими с использованием ONпредложения, содержащего все ключевые столбцы) оптимизатор запросов может упростить большую часть общей логики, что приводит к сравнительно простым планам, которые делают не требует, чтобы Split-Sort-Collapse или Segment-Sequence Project проверяли, что целевые строки касаются только один раз.
В сложных 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])
...
Это приводит ко многим дополнительным вычисляемым столбцам, в первую очередь связанным с разделением и данными, необходимыми при преобразовании обновления в пару вставка / обновление. Эти дополнительные столбцы приводят к тому, что промежуточная строка превышает разрешенные 8060 байтов при более ранней сортировке - той, что сразу после фильтра:

Обратите внимание, что Фильтр имеет 1319 столбцов (выражений и базовых столбцов) в своем списке вывода. Присоединение отладчика показывает стек вызовов в точке, где возникает фатальное исключение:

Заметим попутно , что проблема заключается не в Spool - исключение там преобразуется в предупреждение о потенциале для строки , чтобы быть слишком большим.
Почему обновление с использованием слияния не удается, в то время как вставка делает, и прямое обновление также делает?
Прямое обновление не имеет такой же внутренней сложности, как MERGE. Это принципиально более простая операция, которая имеет тенденцию упрощать и оптимизировать лучше. Удаление NOT MATCHEDпредложения может также устранить достаточную сложность, так что в некоторых случаях ошибка не генерируется. Однако этого не произойдет.
В конечном счете, мой совет - избегать MERGEбольших или более сложных задач. По моему опыту, отдельные операторы вставки / обновления / удаления, как правило, лучше оптимизируются, их легче понять, а также они в целом работают лучше по сравнению с MERGE.