在下面的DELETE
语句中,我尝试删除除按某些标准排序的第一行之外的所有行。(实际查询更有意义,这只是一个重现。所有的sys.objects
东西都只是为了生成测试数据。)
注意过滤器r <> 1
。然而,该OUTPUT
子句输出已删除的行r = 1
。怎么会这样?
USE tempdb
SET XACT_ABORT ON
BEGIN TRAN
SELECT *
INTO #o
FROM sys.objects
SELECT TOP 2 name FROM #o ORDER BY object_id --debug output
DELETE k
OUTPUT Deleted.name, Deleted.r
FROM (
SELECT k.*, ROW_NUMBER() OVER (ORDER BY object_id) r
FROM #o k
) k
WHERE r <> 1 --OUTPUT returns rows with (r = 1)
SELECT TOP 2 name FROM #o ORDER BY object_id --debug output
ROLLBACK
查询结果:
(第三个结果集是完整的 - 只有一行。)
请注意,除第一行之外的所有行都已删除。列的编号顺序r
似乎与请求的不匹配。并且有一行r = 1
。
这是启用了跟踪标志 4199 的 SQL Server 2014 CU3。
这是目前的预期行为
所以它实际上的行为是这样的(伪代码)
尽管这是当前定义的预期行为,但这并不合理,他们说
我尚未在 SQL Server 2014 上进行测试,但在 2012 年,计划如下所示。
在删除运算符(左侧)之后,已删除行的列值
object_id
重新排序并重新应用 row_number。从结果来看,您的情况似乎也发生了同样的情况。(临时表的 id 为负数,并排在最前面,
sysrscols
其正object_id
数较低3
)。除了结果的可疑语义外,第二个排序依据
object_id
在这个计划中似乎并不是绝对必要的,因为看起来它们很可能在任何情况下都已经按该顺序排序了。关于这种特定情况的解决方法,将输出子句更改为
OUTPUT Deleted.name, 1 + Deleted.r AS r
可行。对于更复杂
WHERE
的子句,我认为您需要通过来计算 row_number,然后再进行连接。例如结果
你真的需要 ROW_NUMBER 和 OUTPUT 吗?一个不错的基于集合的 DELETE 怎么样,例如这样的: