如果语句包含没有 INTO 子句的 OUTPUT 子句,则 DML 语句的目标表“RECEIPT”不能有任何启用的触发器

Posted

技术标签:

【中文标题】如果语句包含没有 INTO 子句的 OUTPUT 子句,则 DML 语句的目标表“RECEIPT”不能有任何启用的触发器【英文标题】:target table `RECEIPT` of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause 【发布时间】:2016-01-15 08:56:33 【问题描述】:

我有一个名为INVOICE 的表,它存储有关订单/订单的账单信息,该表中的一列是名为paid 的列,它是一种位。顾名思义,此列表示特定订单/订单账单是否已支付。

我有另一个名为 RECEIPT 的表,该表存储有关特定发票的任何付款流程的信息。

因此,每次用户为指定发票支付金额时,都会创建一个新的收据记录。

现在我要做的是创建一个触发器,更新 INVOICE 表中的 paid 列并将其设置为 1。如果收据总和属于发票等于INVOICE表中的amount_due

换句话说,如果发票到期金额 = 100$ 用户支付了 50 美元 然后,迟到了,他付了另外 50 美元 INVOICE 表中的paid 列应设置为 1,因为总付款等于发票到期金额

这是我为实现上述目的而创建的触发器

CREATE TRIGGER tg_invoice_payment ON RECEIPT
AFTER INSERT
AS
BEGIN
UPDATE INVOICE
SET paid = 1 
WHERE INVOICE.invoice_id = (SELECT inserted.invoice_id FROM inserted)
AND (SELECT SUM(RECEIPT.amount_paid) 
FROM RECEIPT 
JOIN inserted ON RECEIPT.receipt_id = inserted.receipt_id 
WHERE RECEIPT.invoice_id = inserted.invoice_id) = (SELECT INVOICE.amount_due 
FROM INVOICE 
JOIN inserted ON INVOICE.invoice_id = inserted.invoice_id 
WHERE INVOICE.invoice_id = inserted.invoice_id)
END;

编译成功,但在运行时出现以下错误:

【问题讨论】:

问题出在您没有向我们展示的一段代码中,即执行INSERT ... OUTPUT ...,并且,正如错误消息也指出(很清楚,IMO),它没有使用INTO. 另外,你的触发器坏了。当inserted 包含多行时,INVOICE.invoice_id = (SELECT inserted.invoice_id FROM inserted) 将产生错误。 收据表是否有自动递增的标识列?请参考:sql-server-helper.com/error-messages/msg-334.aspx 【参考方案1】:

我个人认为您应该在触发器范围之外更新paid 状态。如果您在RECEIPT 中执行INSERT,则可以在此之后立即执行UPDATE INVOICE ... 语句(当然是在TRANSACTION 中)。这样会更干净、更可预测。

关于您遇到的错误,根据您提供给我们的信息,很难说出是什么原因造成的。也许TRIGGER 正在触发其他TRIGGERs,从而产生您遇到的错误?您提供的语句根本没有OUTPUT 语句。

在任何情况下,您提供的语句都没有正确编写(正如 Damien 指出的那样),因为 inserted 表可以有多行。这是为了纠正至少那部分的重写:

CREATE TRIGGER tg_invoice_payment ON RECEIPT
AFTER INSERT
AS
BEGIN
    UPDATE
        INVOICE
    SET 
        paid = 1
    FROM
        inserted AS ins
        INNER JOIN INVOICE AS inv ON
            inv.invoice_id=ins.invoice_id
    WHERE
        inv.amount_due=(
            SELECT 
                SUM(r.amount_paid) 
            FROM 
                RECEIPT AS r
            WHERE
                r.receipt_id=ins.invoice_id
        );
END;

但正如我之前提到的,您可能不会通过TRIGGER 执行此操作。在任何INSERT/UPDATE 之后立即从您的程序中执行此语句。或者,编写一个用于插入RECEIPT 的存储过程,并在INSERT 之后立即执行UPDATE 语句。

【讨论】:

非常感谢,我对 SQL 语法不是很熟悉,但我从错误中了解到,如果有 OUTPUT 语句,则必须有 INTO 子句,但实际上我的代码和你的不包含任何OUTPUT 语句。我不知道我是否理解正确,再次感谢@TT @AliAlhamaly 此语句中没有 OUTPUT 子句。这是您的线索,您应该在其他地方寻找您遇到的问题的原因。 GL! 我想我找到了问题的原因,RECEIPT 表中的 id 列是一个自动递增的标识列。在 google 上进行一些搜索,我发现包含更新语句的启用触发器无法在包含此类 id 列的表上触发,我的意思是自动递增的标识表。所以我现在需要解决这个问题来绕过这个错误。但仍然不确定这是否是真正的原因 这个*** thread 似乎是关于你的情况。您正在插入/更新收据,并且插入语句显然有一个 OUTPUT 子句。要么在语句中添加INTO ...,要么重写语句以不包含 OUTPUT 子句。

以上是关于如果语句包含没有 INTO 子句的 OUTPUT 子句,则 DML 语句的目标表“RECEIPT”不能有任何启用的触发器的主要内容,如果未能解决你的问题,请参考以下文章

修改要插入的数据的触发器不起作用(语句包含没有 INTO 子句的 OUTPUT 子句)

OUTPUT INTO 子句中可以使用哪些列?

SQL语法 之 操作语句

选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中

使用 OUTPUT 子句从 SELECT INSERT 语句中检索原始身份和新身份映射

如何修复此 SELECT 语句中的错误 INTO 子句