2010-12-03 39 views
2

我一直在修补这个问题一段时间,但没有任何工作适合我。SQL Server 2008 - 使用本地变量进行INSERT和UPDATE触发器

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

但是,这给了我一个错误。

任何帮助,将不胜感激!

+0

感谢您的帮助@Dog Ears和@Martin!你是对的,这是一个任务:p但希望有一天我会达到一个水平,我可以为他人的家庭作业给出我自己的答案。 :D哦,是的,我有触发器工作!我在@Dog Ears的建议中提到加入产品并插入表格:DECLARE \t @stock int DECLARE \t @neworder \t int SELECT \t @stock = products.quantity_in_stock从\t产品插入WHERE products.product_id = inserted.product_id SELECT \t @neworder = inserted.quantity从\t插入触发器工作!再次感谢您的回复!^_^ – Magi604 2010-12-04 07:37:05

回答

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

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); 

这“工作”(在长期的lossest感) 考虑到马丁的评论为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 

,并解决马丁斯第一点这种方式较好:

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 
+0

看起来更好!实际上,我发现的多行插入的另一个问题是,它们可能包含需要分组的同一产品的多行。例如`选择i.p​​roductid从插入我加入产品p在i.productid = p.productid组由i.productid具有SUM(i.quantity)> p.quantity_in_stock` – 2010-12-04 01:15:49

0

另一种解决方案是使用的INSTEAD OF触发器,这样的事情:

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 

这触发行为与其他建议不同!这个触发器会插入完成的订单,并忽略超过库存水平的订单,这可能不是必需的[事实上它可能不是在大多数情况下;你的应用程序想知道什么时候订单还没有保存到数据库!!!]

注意这只是一个插入,你需要创建一个不同的触发器更新为'插入'值将需要更新不插入。

此外,还有其他一些考虑因素超出了这个问题的范围。您应该在插入订单时降低库存水平,并且您应该可能想要处理为相同产品插入多个明细行的情况。