带有窗口函数的 OUTPUT 子句

Cha*_*ace 7 sql-server window-functions output-clause

该条款中是否存在未记录的限制OUTPUT,或者这是一个错误?

给出下表:

CREATE TABLE t1 (SomeId int, flag bit, value int);
Run Code Online (Sandbox Code Playgroud)

我想在UPDATE语句中使用计算值,然后使用OUTPUT输出该值。请注意,计算值未在该部件中使用SET,这可能允许通过输出左侧列来解决问题。

以下工作正常,它是一个完美标准的可更新子查询(派生表)。

UPDATE subT1
SET flag = 1
OUTPUT inserted.SomeValue
FROM (
    SELECT *,
      SomeValue = t1.value + 123
    FROM t1
) subT1;
Run Code Online (Sandbox Code Playgroud)

然而使用窗口函数会出现一个奇怪的错误:

UPDATE subT1
SET flag = 1
OUTPUT inserted.Sum
FROM (
    SELECT *,
      Sum = SUM(t1.value) OVER (PARTITION BY t1.SomeId)
    FROM t1
) subT1;
Run Code Online (Sandbox Code Playgroud)
Msg 404 Level 16 State 1 Line 3
The column reference "inserted.Sum" is not allowed because it refers to a base table that is not being modified in this statement.
Run Code Online (Sandbox Code Playgroud)

这是没有意义的,因为我们已经确定可以使用 来引用计算列inserted

好吧,让我们尝试使用OUTPUT subT1.Sum无论如何,而不是inserted.Sum

Msg 4104 Level 16 State 1 Line 3
The multi-part identifier "subT1.Sum" could not be bound.
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为该表引用已被修改,并且文档说:

如果正在修改的表也在 FROM 子句中指定,则对该表中的列的任何引用都必须使用 INSERTED 或 DELETED 前缀进行限定。


同时,如果我在第二个表上使用联接:

CREATE TABLE t2 (SomeId int, flag bit, value int);
Run Code Online (Sandbox Code Playgroud)

效果很好

UPDATE t1
SET flag = 1
OUTPUT subT2.Sum
FROM t1
JOIN (
    SELECT t2.*,
      Sum = SUM(t2.value) OVER (PARTITION BY t2.SomeId)
    FROM t2
) subT2 ON subT2.SomeId = t1.SomeId;
Run Code Online (Sandbox Code Playgroud)

数据库<>小提琴

那么,窗口函数引发该错误的事实究竟是一个错误,还是一个未记录的限制?

话虽如此,子查询内的联接也是不允许的,因此很可能这是一个未记录的限制。

UPDATE subT1
SET flag = 1
OUTPUT inserted.Sum
FROM (
    SELECT t1.*,
      Sum = t2.value
    FROM t1
    JOIN t2 ON t2.SomeId = t1.SomeId
) subT1;
Run Code Online (Sandbox Code Playgroud)
Msg 404 Level 16 State 1 Line 3
The column reference "inserted.Sum" is not allowed because it refers to a base table that is not being modified in this statement.
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 4

我没有看到文档中明确涵盖这一点,但OUTPUT deleted.Sum 确实有效

这确实有一定意义,因为它SUM是根据“更新前”而不是“更新后”的值计算的

即下面返回 a SUMof2而不是0

CREATE TABLE t1 (SomeId int, flag bit, value int);

INSERT T1 values (1,1,1), (1,1,1);

UPDATE subT1
SET flag = 1, value = 0
OUTPUT deleted.Sum AS [SUM(value)],
       deleted.value AS [deleted.value], 
       inserted.value AS [inserted.value]
FROM (
    SELECT *,
      Sum = SUM(t1.value) OVER (PARTITION BY t1.SomeId)
    FROM t1
) subT1;

DROP TABLE t1 
Run Code Online (Sandbox Code Playgroud)
总和(值) 删除的值 插入值
2 1 0
2 1 0

数据库<>小提琴

对于更简单的表达式,SomeValue = t1.value + 123它能够通过在输出流上添加计算标量inserted.SomeValue来计算不同deleted.SomeValue,但可能窗口函数也太复杂而无法做到这一点。

即下面的...


CREATE TABLE t1 (SomeId int, flag bit, value int);

INSERT T1 values (1,1,1), (1,1,1);

UPDATE subT1
SET flag = 1, 
     value = 999
OUTPUT deleted.SomeValue AS [deleted.SomeValue],
       inserted.SomeValue AS [inserted.SomeValue]
FROM (
    SELECT *,
      SomeValue = t1.value + 123
    FROM t1
) subT1;

DROP TABLE t1 
Run Code Online (Sandbox Code Playgroud)

退货

已删除.SomeValue 插入的.SomeValue
124 1122
124 1122

并且执行计划是一样的

UPDATE t1
SET flag = 1, 
     value = 999
OUTPUT deleted.value  + 123 AS [deleted.SomeValue],
       inserted.value  + 123 AS [inserted.SomeValue]
FROM t1;
Run Code Online (Sandbox Code Playgroud)