2010-12-03 9 views
2

J'ai bricolé ce problème pendant un moment, mais rien ne fonctionne pour moi.SQL Server 2008 - utilisation de variables locales pour un déclencheur INSERT et UPDATE

La question est de créer un INSERT et déclencheur mise à jour (tr_check_qty) pour la table Order_Details pour permettre que les commandes de produits qui ont une quantité en stock supérieure ou égale aux unités commandées.

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 

Pour tester ce déclencheur, nous sommes censés utiliser cette requête:

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

La requête sélectionne un produit qui a seulement 28 quantity_in_stock, ce qui devrait déclencher la gâchette. Mais mon déclencheur ne se déclenche pas et il met à jour la table avec succès.

Je soupçonnais que les déclencheurs n'aiment pas les variables locales, donc j'essayé de ne pas en utilisant des variables locales:

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

Mais cela m'a donné une erreur.

Toute aide serait appréciée!

+0

Merci pour votre aide @Dog Ears et @Martin! Vous avez raison, c'est une tâche: p mais j'espère que j'arriverai à un niveau où je pourrai donner mes propres réponses pour les devoirs des autres. : D Oh oui, j'ai la gâchette pour travailler! Je clued sur la suggestion de se joindre au produit et tables inserted de oreilles @Dog: DECLARE \t @stock int DECLARE \t @neworder \t int SELECT \t @stock = products.quantity_in_stock DE \t produits, inséré OU products.product_id = inserted.product_id SELECT \t @neworder = inséré.quantité FROM \t inserted Le trigger a fonctionné! Merci encore pour vos deux réponses!^_^ – Magi604

Répondre

1
  1. Vous supposez qu'il n'y aura qu'une seule insertion ou mise à jour de ligne.

  2. n'a pas de prédicat - probablement il faut vérifier le niveau de stock du productid inséré? Si oui, quelle est la structure de la table products? (Au moment @stock sera attribué une valeur à partir d'une ligne arbitraire en supposant plus d'une ligne dans le tableau products.

  3. Cela ne fonctionnera pas sous isolement de capture instantanée.

Pour contourner # 1 et # 2 vous devez joindre à la table inserted sur la table products en utilisant productid ou autre chose et voir si toutes les lignes où existent inserted.quantity > products.quantity_in_stock

pour quelques idées sur # 3 lire the discussion here

0

Votre déclencheur est pas loin, mais vraiment vous pourrait utiliser et déclencheur INSTEAD OF

Créer des données de test

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

Cette 'marche' (au sens lossest du terme En prenant en compte les commentaires de Martin, le productid pour le quantity_in_stock a dû être déterminé.

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 

Ces maintenant fonctionnent tous comme prévu ...

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 

Et pour aborder le premier point Martins cette approche est meilleure:

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

Ça a l'air mieux! En fait, un autre problème avec les inserts multirow qui vient de se produire serait qu'ils pourraient contenir plusieurs lignes pour le même produit qui aurait besoin d'être groupé. par exemple. 'choisir i.productid de inséré je joins le produit p sur i.productid = p.productid le groupe par i.productid ayant SUM (i.quantity)> p.quantity_in_stock' –

+0

@Martin - je laisserai cela comme un exercice pour le lecteur .. comme c'est clairement devoirs .. :) –

+0

Merci pour votre aide @ Dog Ears et @ Martin! Vous avez raison, c'est une tâche: p mais j'espère que j'arriverai à un niveau où je pourrai donner mes propres réponses pour les devoirs des autres. : D Oh oui, j'ai la gâchette pour fonctionner! Je clued sur la suggestion de se joindre à oreilles @Dog le produit et les tables insérés: DECLARE \t \t @stock \t \t int DECLARE \t \t @neworder \t int SELECT \t \t @stock = products.quantity_in_stock DE \t \t produits, insérés OÙ \t \t products.product_id = inserted.product_id SELECT \t \t @neworder = inserted.quantity DE \t \t inséré La gâchette a fonctionné! Merci encore pour vos deux réponses!^_ ^ – Magi604

0

Une autre solution consiste à utiliser le lieu de détente, quelque chose comme ceci:

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 

Ce déclencheur se conduit différemment de l'autre suggestion! Ce déclencheur va insérer les commandes qui sont remplies et ignorer les commandes qui dépassent le niveau de stock, ce n'est peut-être pas nécessaire [en fait, il n'est probablement pas dans la plupart des situations; votre application voudrait savoir quand une commande n'a pas été sauvegardée dans la base de données!]

Note Remarque: il s'agit simplement d'une insertion nécessaire pour créer un déclencheur différent pour la mise à jour en tant que valeurs 'insérées' aurait besoin d'être mises à jour pas d'inserts.

Encore une fois il y a d'autres considérations en dehors de la portée de cette question .. vous devriez probablement réduire le niveau de stock à mesure que les commandes sont insérées et vous devriez être capable de gérer des situations où plusieurs rangées sont insérées pour un même produit .