SQL Server 2008 - 使用局部变量进行INSERT和UPDATE触发器

时间:2010-12-03 23:34:09

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

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

问题是为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)

但这给了我一个错误。

任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:2)

  1. 您假设只有一行插入或更新。

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

  3. 这在快照隔离下无效。

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

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

答案 1 :(得分:0)

你的触发器并不遥远,但你真的可以使用和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的评论,需要确定productid的{​​{1}}。

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

答案 2 :(得分:0)

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

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

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

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

此问题的范围之外还有其他一些注意事项。您可能应该在插入订单时减少库存水平,并且您可能希望处理为同一产品插入多个详细信息行的情况。