Обновление поведения

Когда я ошибся в тексте запроса на обновление, я обнаружил непредсказуемый результат запроса. Вот текст запроса для обновления.

DECLARE @T TABLE (Id int,[Name] nvarchar(100),RNA int)

INSERT INTO @T(Id,[Name])
SELECT [Id],[Name] 
FROM (VALUES (1,N'D'),
             (2,N'B'),
             (3,N'S'),
             (4,N'A'),
             (5,N'F')
     ) AS vtable([Id],[Name])

UPDATE @T
SET RNA = T.RN
FROM (
  select PP.Name,ROW_NUMBER() OVER(ORDER BY PP.Name) RN,PP.RNA from @T PP
) T

select * from @T

Я знаю, где была допущена ошибка:

UPDATE @T

должно быть

UPDATE T

Но почему результат (с «плохим» запросом) выглядит так:

Id   Name  RNA
---- ----- -------
1    D     1
2    B     5
3    S     1
4    A     5
5    F     1

Я подозреваю, что 1 и 5 значений MIN (Id) и MAX (Id). План выполнения выглядит так:

Будет ли такая ситуация одинаковой в каждой ситуации с такой ошибкой?

Если да, имеет ли это поведение какое-либо практическое значение?

4
nl ja de

1 ответы

Ситуация не будет одинаковой для всех ошибок. У вас есть не-детерминированный оператор обновления, т. Е. Теоретически любое из значений для RN в вашем подзапросе T может быть применено к любому из значений в @T . В основном вы используете версию UPDATE :

SELECT  *
FROM    @t a
        CROSS JOIN
        (   SELECT  TOP 1
                    PP.Name,
                    ROW_NUMBER() OVER(ORDER BY PP.Name) RN,
                    PP.RNA 
            FROM    @T PP
            ORDER BY NEWID()
        ) T
OPTION (FORCE ORDER);

В онлайн-руководстве говорится:

Результаты инструкции UPDATE не определены, если оператор   включает предложение FROM, которое не указано таким образом, что только   доступно одно значение для каждого обновленного столбца,   то есть если оператор UPDATE не является детерминированным.

Интересно то, что если вы запустите выше, каждый раз вы получите другой результат (запрет 1/25 получить один и тот же результат дважды подряд), если вы удалите случайную сортировку с помощью NEWID() вы получите то же значение RN для каждой строки, но обновление последовательно возвращает те же результаты, с двумя разными RN s. Я не удивлен, что результат не согласуется с каким-либо случайным порядком, потому что без изменений в данных и без случайного фактора я ожидал бы, что оптимизатор придумает один и тот же план выполнения независимо от того, сколько раз он запускается.

Поскольку в вашем запросе обновления не указано явное упорядочение, порядок определяется порядком записей на листе, если порядок записей изменяется, результат изменяется. Это можно показать, вставив записи @T в новую таблицу с разными идентификаторами

DECLARE @T2 TABLE (Id int,[Name] nvarchar(100),RNA int);
INSERT @T2 
SELECT  id, Name, NULL
FROM    @T
ORDER BY ROW_NUMBER() OVER(ORDER BY  NEWID())
OPTION (FORCE ORDER);

UPDATE @T2
SET RNA = T.RN
FROM (
  select PP.Name,ROW_NUMBER() OVER(ORDER BY PP.Name) RN,PP.RNA from @T2 PP
) T

SELECT  *
FROM    @T2;

Я не вижу причин, почему это всегда минимальное или максимальное значение RN , хотя, я думаю, вам нужно будет углубиться в оптимизатор, чтобы найти это. Вероятно, это новый вопрос, который лучше подходит для обмена стеками dba .

2
добавлено
SqlCom.ru - Стиль жизни SQL
SqlCom.ru - Стиль жизни SQL
908 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только MS SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz). Предложения в @sql_ninja

SQL_Ninja
SQL_Ninja
340 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz)