【问题标题】:SQL Trigger AFTER INSERT is not following my IF statements and not deleting the new rowSQL 触发器 AFTER INSERT 没有遵循我的 IF 语句并且没有删除新行
【发布时间】:2021-07-07 08:53:24
【问题描述】:

我创建了一个触发器,每次我插入“发票”表时,它都会检查数量(订购的数量)是否高于其他表中可用的库存数量。 如果数量较高,则应打印库存不足并删除新插入的行。 它根本没有这样做。它只是插入行,并没有打印出任何消息。

create trigger testtrigger
on invoices
after insert
as
begin
declare @productID int
declare @quantity int
declare @stock int
select @stock = stock from Products where ProductID = @productID
select @productID = productid from inserted;
select @quantity = Quantity from inserted;
if @productID not in (1, 2, 3, 4, 5, 6) 
    begin
        if @quantity > @stock
        begin
            Print N'Not enough Stock. Current Stock: ' + @stock
            delete from Invoices where InvoiceID = @@IDENTITY
        end
    end
end

前 6 种产品不是有库存量的东西,它是一种服务,所以我不希望它在这些产品 ID 的订单上触发。

这只是检查更多事情的更大 If Else 的一部分(例如,如果数量少于库存,则更新剩余库存的 else 语句,但这是触发器的开始,它不起作用所以我想我从这里开始。

【问题讨论】:

  • 您的触发器存在严重缺陷;它假设 INSERT 只包含 1 行。那明显是错的。此外,通常情况下,您不会DELETE 行,PRINT 信息消息,您会THROW 错误。
  • 如果同一产品也插入了 2 行,会发生什么情况?如果 other 表的值是 20,并且插入的两个行的值都是 15 ,那么两者都小于20,但是,您肯定没有库存中的30。有人会期望 other 表的(股票?)列应该同时更新。
  • 如果我抛出而不是删除,它仍然会删除插入的行吗?我不确定如何更改它以适应批量插入:/有没有办法让它逐行读取插入?
  • "有没有办法让它逐行读取插入内容?" 不要那样做,触发器的影响应该是最小尽可能在 DML 声明上;遍历插入的数据对于性能来说是糟糕的
  • "如果我使用 throw 而不是 delete,它还会删除插入的行吗?" 假设您 THROW 的错误具有足够高的严重性并且正在自动处理回滚,或在您的TRY...CATCH 是的。但是您在这里提出的问题会导致更多问题;你的想法有几个缺陷。

标签: sql sql-server tsql triggers


【解决方案1】:

对于任何 T-SQL,包括触发器,你需要进入基于集合的操作的心态,不是过程,RBAR(Row By Agonizing Row),操作。

触发器为您提供Inserted (and for update/delete triggers Deleted pseudo tables,您应该像使用任何其他表一样使用它,即以基于集合的方式。

正如 cmets 中所指出的,触发器需要尽可能快地运行,因为它们在您进行处理时会持有锁。

create trigger testtrigger
on invoices
after insert
as
begin
    set nocount, xact_abort on;

    -- Check if *any* of the inserted products exceeds the current stock levels
    -- Taking into account the possibility of the same product occurring in multiple invoices
    -- You could use similar logic to capture the products for which the stock level was exceeded
    -- However its very complex to only rollback those which are wrong.
    -- Really this should be your last line of defence, only used to ensure database integrity, so you should be checking stock levels before now
    -- And therefore it shouldn't matter rolling everything back
    if exists (

        select 1
        from Inserted I
        inner join Products P on P.ProductId = I.productId
        where productID not in (1, 2, 3, 4, 5, 6)
        group by P.Id, P.Stock
        having sum(I.Quantity) > P.Stock;

    ) begin
        -- If you want to do something in your calling code you need to throw an exception. Printing is only relevent while testing in SSMS.
        -- Throwing an exception will also roll back the transaction
        throw 51000, 'Not enough Stock.', 0;
    end;

end;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多