Причина, по которой итератор Merge выводит пустую строку для столбца Type
, интересна.Оптимизатор распознает, что Type
является постоянным значением, и применяет перезапись, которая удаляет этот столбец из потока, добавляя его позже в качестве вычисляемого скаляра.Это можно увидеть в действии, добавив предложение OUTPUT
в оператор MERGE
, чтобы выдать значение inserted.[Type]
.Без условия OUTPUT
нет необходимости в Compute Scalar, поэтому он оптимизирован, что оставляет нас с формой плана, показанной в исходном примере.
Ошибка возникает, когда что-то между итератором Merge (которыйтакже может быть слиянием таблицы, а не слиянием кластерного индекса), а для вычисления скаляра требуется значение столбца [Type]
.Так как он был удален из потока (несмотря на то, что он показан в плане как выход из слияния), мы в конечном итоге ссылаемся на то, что не существует, и которое создает пустую строку.В другом мире вместо этого SQL Server будет утверждать что-то вроде нарушения нулевого указателя, но это уже другая история.
Эта проблема немного исправлена в SQL Server 2012, но все еще существует ошибка.Я говорю «немного» исправлено, потому что перезапись, которая удаляет столбец с постоянными значениями, отключена (поэтому проверка FK получает реальное значение для поиска, а не пустую строку), но Compute Scalar, который добавляет строку «Car» обратнов поток все еще появляется, если OUTPUT inserted.[Type]
добавлено.В идеальном мире план будет просто выводить значение, предоставленное слиянием, а не пересчитывать константу.В любом случае, это не так уж важно (просто показывает, что реализация по-прежнему немного ненадежна), но есть ошибка, связанная с удалением ссылки на столбец:
Он воспроизводится только в SQL 2012 с таблицейпеременная (все остальные типы таблиц в порядке), но воспроизводится во всех выпущенных версиях SQL Server с любым типом табличного объекта:
DECLARE @Bug TABLE
(
id INTEGER PRIMARY KEY,
data AS 'X' PERSISTED,
CHECK (data = 'A')
)
MERGE @Bug AS b USING (VALUES(1)) AS u (id) ON u.id = b.id
WHEN NOT MATCHED THEN INSERT (id) VALUES (u.id)
OUTPUT INSERTED.data;
Дело в том, что ограничение CHECK
пропущено - оператор Assertон проверяет, что он добавляется в план, но затем оптимизируется, когда оптимизатор видит столбец с постоянными значениями и применяет его перезапись.Удаление Assert позволяет добавить значение «X» в таблицу, даже если оно нарушает ограничение CHECK.Как я уже сказал, вы можете воспроизвести это в 2008 R2 и более ранних версиях с реальными и временными таблицами - в 2012 году это только ошибки с табличными переменными.
Пол