Почему второй раз, когда я попытался объединить ту же строку, которая уже была вставлена, это привело к ошибке. Если эта строка превысила максимальный размер строки, можно было бы ожидать, что ее невозможно будет вставить в первую очередь.
Во-первых, спасибо за сценарий воспроизведения.
Проблема не в том, что 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
.