usr*_*usr 6 sql-server sql-server-2014
在以下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
Run Code Online (Sandbox Code Playgroud)
查询结果:

(第三个结果集是完整的 - 只有一行。)
请注意,除第一行之外的所有行都已删除。列的编号顺序r似乎与请求的不匹配。并且有一排r = 1。
这是启用了跟踪标志 4199 的 SQL Server 2014 CU3。
Mar*_*ith 12
这是预期的行为,在目前
该函数在 DELETE 流上进行评估。
所以它实际上表现得像这样(伪代码)
DELETE k
OUTPUT Deleted.name,
ROW_NUMBER() OVER (ORDER BY Deleted.object_id) as 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)
Run Code Online (Sandbox Code Playgroud)
尽管这是当前定义的预期行为,但实际上并不合理,他们说
从长远来看,我们需要实际修复 OUTPUT 子句的行为以匹配 ANSI SQL 标准的行为,这将导致结果的变化。因此,我们将查看 SQL Server 未来版本的正确语义,因为可能存在依赖于当前行为的应用程序。
我没有在 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 然后进行连接。例如
ALTER TABLE #o
ADD CONSTRAINT PK PRIMARY KEY (object_id);
WITH k AS
(
SELECT *,
ROW_NUMBER() OVER (ORDER BY object_id) r
FROM #o
)
MERGE k AS k1
using k AS k2
ON k1.object_id = k2.object_id
WHEN matched AND k2.r <> 1 THEN
DELETE
OUTPUT Deleted.name,
k2.r;
Run Code Online (Sandbox Code Playgroud)

结果

| 归档时间: |
|
| 查看次数: |
2647 次 |
| 最近记录: |