4

当我在更新查询文本中出错时,我发现了不可预测的查询结果。这是更新的查询文本。

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

1 回答 1

2

对于每一种错误,情况都不会相同。您有一个不确定的更新语句,也就是说,理论上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每一行,但更新始终返回相同的结果,有 2 个不同RN的 s。我并不惊讶结果保持一致,没有随机排序,因为没有更改数据,也没有引入随机因素,我希望优化器无论运行多少次都能提出相同的执行计划。

由于更新查询中没有指定明确的顺序,因此顺序是由于叶子上记录的顺序,如果记录的顺序改变了,结果也会改变。这可以通过将 的记录插入@T到具有不同 ID 的新表中来显示

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 堆栈交换的新问题。

于 2013-01-29T09:19:18.707 回答