【问题标题】:SQL Server 2008 - using local variables for an INSERT and UPDATE triggerSQL Server 2008 - 为 INSERT 和 UPDATE 触发器使用局部变量
【发布时间】:2010-12-03 23:34:09
【问题描述】:

我一直在修补这个问题一段时间,但没有什么对我有用。

问题是为 order_details 表创建一个 INSERT 和 UPDATE 触发器 (tr_check_qty),以仅允许订购库存数量大于或等于订购单位的产品。

CREATE TRIGGER tr_check_qty

ON order_details
FOR insert, update

AS

DECLARE @stock int
DECLARE @neworder int
SELECT @stock = quantity_in_stock FROM products
SELECT @neworder = quantity FROM inserted

IF @neworder > @stock

BEGIN
PRINT 'NO WAY JOSE'
ROLLBACK TRANSACTION
END

为了测试这个触发器,我们应该使用这个查询:

UPDATE order_details
SET quantity = 30
WHERE order_id = '10044'
AND product_id = 7

查询选择了一个只有 28 个 quantity_in_stock 的产品,它应该触发触发器。但是我的触发器没有触发,它成功更新了表。

我怀疑触发器不喜欢局部变量,所以我尝试不使用局部变量:

(SELECT quantity FROM inserted) > (SELECT quantity_in_stock FROM products)

但这给了我一个错误。

任何帮助将不胜感激!

【问题讨论】:

  • 感谢@Dog Ears 和@Martin 的帮助!你是对的,这是一项作业:p 但希望有一天我能达到一个水平,我可以为其他人的家庭作业给出自己的答案。 :D 哦,是的,我的扳机开始工作了!我在@Dog Ears 建议加入产品和插入表: DECLARE @stock int DECLARE @neworder int SELECT @stock = products.quantity_in_stock FROM products,inserted WHERE products.product_id = inserted.product_id SELECT @neworder = 插入。插入的数量 触发器起作用了!再次感谢您的两位回复! ^_^

标签: sql-server sql-server-2008 triggers


【解决方案1】:
  1. 您假设只有单行插入或更新。

  2. quantity_in_stock FROM products 没有谓词 - 大概需要检查插入的 productid 的库存水平?如果是这样,products 表的结构是什么? (目前@stock 将从任意行中分配一个值,假设products 表中有不止一行。

  3. 这在快照隔离下不起作用。

要绕过#1 和#2,您需要使用productid 或其他任何方式将inserted 表加入products 表,并查看inserted.quantity > products.quantity_in_stock 处是否存在任何行

关于#3 的一些想法请阅读the discussion here

【讨论】:

    【解决方案2】:

    您的触发器并不遥远,但实际上您可以使用 INSTEAD OF 触发器

    创建测试数据

    create table product ( productId int identity(1,1) constraint PK_product_productId primary key clustered, quantity_in_stock int )
    create table order_detail (  order_id int
                            ,productId int constraint FK_order_product_productId foreign key references product (productId)
                            ,quantity int not null)
    set identity_insert product on
    insert into product (productId, quantity_in_stock) values ( 1, 100 ), ( 2, 25 ) , (3, 2);
    

    这个“有效”(在术语的损失意义上) 在 Martin 的 cmets 上,productid 需要确定 quantity_in_stock。

    CREATE TRIGGER tr_check_qty
    ON order_detail
    FOR insert, update AS
    
    DECLARE @stock int
    DECLARE @neworder int
    SELECT @stock = quantity_in_stock 
            From product 
            Where productid = (select productid from inserted)
    SELECT @neworder = quantity FROM inserted
    
    IF @neworder > @stock
    
    BEGIN
    PRINT 'NO WAY JOSE'
    ROLLBACK TRANSACTION
    END
    

    这些现在都按预期工作......

    INSERT order_detail (order_id, productId, quantity)
    values 
     (10044, 1, 30) -- works as stock is 100
    ,(10044, 3,  1)
    
    insert order_detail (order_id, productId, quantity)
    values 
        (10044, 1, 130) /* fails (CORRECTLY) WITH Msg 3609, Level 16... (transacted ended in the trigger..) */
    
    /* this should work... */
    UPDATE order_detail
    SET quantity = 30
    WHERE order_id = 10044
    AND productid = 1
    
    /* this should fail..  */
    UPDATE order_detail
    SET quantity = 3000 /*< not enough stock. */
    WHERE order_id = 10044
    AND productid = 1
    

    为了解决 Martins 的第一点,这种方法更好:

    CREATE TRIGGER tr_check_qty
    ON order_detail
    FOR insert, update AS
    
    DECLARE @stock int
    DECLARE @neworder int
    
    if(exists(select * 
              from inserted i join product p on i.productId = p.productId 
              where i.quantity > p.quantity_in_stock)) 
    begin 
    PRINT 'NO WAY JOSE'
    ROLLBACK TRANSACTION
    End
    

    【讨论】:

    • 看起来更好!实际上,我刚刚想到的多行插入的另一个问题是它们可能包含需要分组的同一产品的多行。例如select i.productid from inserted i join product p on i.productid = p.productid group by i.productid having SUM(i.quantity) &gt; p.quantity_in_stock
    • @Martin - 我将把它作为练习留给读者......因为这显然是家庭作业...... :)
    • 感谢@Dog Ears 和@Martin 的帮助!你是对的,这是一项作业:p 但希望有一天我能达到一个水平,我可以为其他人的家庭作业给出自己的答案。 :D 哦,是的,我的扳机开始工作了!我在@Dog Ears 建议加入产品和插入表: DECLARE @stock int DECLARE @neworder int SELECT @stock = products.quantity_in_stock FROM products,inserted WHERE products.product_id = inserted.product_id SELECT @neworder = 插入。插入的数量 触发器起作用了!再次感谢您的两位回复! ^_^
    【解决方案3】:

    另一种解决方案是使用触发器而不是触发器,如下所示:

    Create Trigger TR_Check_Qty
    ON order_detail
    INSTEAD OF insert AS
    
     insert into order_detail (order_id, productId, quantity)
        select i.order_id, i.productId, i.quantity
        from inserted i inner join product p on i.productId = p.productId
        where i.quantity <= p.quantity_in_stock
    

    此触发器的行为与其他建议不同!此触发器将插入已履行的订单并忽略超过库存水平的订单,这可能不是必需的[实际上在大多数情况下可能不需要;您的应用程序想知道订单何时尚未保存到数据库中!!!]

    注意这只是一个插入,您需要为更新创建不同的触发器,因为“插入”值需要是更新而不是插入。

    在此问题的范围之外还有其他考虑因素。您可能应该在插入订单时降低库存水平,并且您可能希望处理为同一产品插入多个详细信息行的情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-30
      • 1970-01-01
      • 2015-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多