1

我的数据库有表 Item、Ingredients、ItemContains、BillItem。项目由一种或多种成分“制成”,并显示在表 ItemContains (IDItem - IDIngredient - ContainingQuantity) 中。当我在 BillItem 中插入记录时,它也有 Quantity (BillItemQuantity) 字段,我希望 Trigger 或 StoredProcedure 减少成分表中 Quantity 字段的值

(IngredientQuantity = IngredientQuantity - BillItemQuantity*ContainingQuantity) ,

但问题是当 Item 包含多个成分时,不知道将 IDIngredient 放在哪里,我尝试使用临时表,但不成功。我为 Oracle 数据库编写了 PL/SQL 触发器,它可以工作,所以如果有人可以帮助将其“转换”为 SQL Server 过程或类似的东西,我将非常感激。谢谢。

create or replace 
trigger ReduceQuantity
before insert on BillItem
for each row
declare
  quaN BillItem.Quantity%type;
  quaO BillItem.Quantity%type;
  idIt integer;
  idItOld integer;
  type RecCon is record (IngID ItemContains.idIngredient%type, Qua ItemContains.quantity%type);
  type TableQuantity is table of RecCon index by binary_integer;
  i binary_integer:=0;

  TabQuant TableQuantity;

begin
  idIt:= :New.IDItem;
  idItOld:= :Old.IDItem;
  quaN := :New.Quantity;
  kolS := :Old.Quantity;

  for rec in (select IDIngredient, Quantity from ItemContains where IDItem = idIt) loop
    TabQuant(i).IngID := rec.IDIngredient;
    TabKol(i).Qua := rec.Quantity;
    i:=i+1;
  end loop;

  if inserting then 
    i:=TabQuant.first;
    while i<=TabQuant.last loop
            update Ingredient set Quantity=Quantity-TabQuant(i).Qua*quaN where IDIngredient = TabQuant(i).IngID;
           i:=TabKol.Next(i);
    end loop; 

  end if;
end ReduceQuantity;

这是您可以在 SQL Fiddle 中运行的整个脚本。它被缩短了,但它完全可以解决这个问题。所以我想在插入新的 BillItem 时触发减少成分表中的数量(例如:BillItem 中的 2 杯卡布奇诺将减少 2 件的“浓缩咖啡”和 0.10 升的“牛奶”)

create table Item(
    IDItem integer NOT NULL,
    Name varchar(30) NOT NULL,
    ItemType varchar(9),
    Unit varchar(5) NOT NULL,
    Price decimal(5,2),
    CONSTRAINT item_PK PRIMARY KEY (IDItem),
    CONSTRAINT item_UQ UNIQUE (Name),
    CONSTRAINT item_unit_CHK CHECK (Unit in ('g', 'kg', 'ml', 'dl', 'l', 'pc')),
    CONSTRAINT item_type_CHK CHECK (ItemType in ('Food', 'Beverage'))
    );

create table Ingredient(
    IDIngredient integer NOT NULL,
    Name varchar(30) NOT NULL,
    Type varchar(9),
    Unit varchar(5) NOT NULL,
    IngredientQuantity decimal(6,2),
    CONSTRAINT ingredient_PK PRIMARY KEY (IDIngredient),
    CONSTRAINT ingredient_UQ UNIQUE (Name),
    CONSTRAINT ingredient_unit_CHK CHECK (Unit in ('g', 'kg', 'ml', 'dl', 'l', 'pc')),
    CONSTRAINT ingredient_type_CHK CHECK (TipSastojka in ('Food', 'Beverage')),
    CONSTRAINT ingredient_UQ1 CHECK(IngredientQuantity>=0)
);

create table ItemContains(
    IDItem integer NOT NULL,
    IDIngredient integer NOT NULL,
    ContainsQuantity decimal(4,2) NOT NULL,
    CONSTRAINT ItemContains_PK PRIMARY KEY (IDItem, IDIngredient),
    CONSTRAINT ItemContains_FK1 FOREIGN KEY (IDItem) references Item(IDItem),
    CONSTRAINT ItemContains_FK2 FOREIGN KEY (IDIngredient) references Ingredient(IDIngredient)
    );

create table Bill(
    IDBill integer NOT NULL,
    BillDate varchar(12) NOT NULL,
    TotalPrice decimal(8,2),
    CONSTRAINT bill_PK PRIMARY KEY (IDBill)
    );


create table BillItem(
    IDBillItem integer NOT NULL,
    IDBill integer NOT NULL,
    IDItem integer NOT NULL,
    BillItemQuantity decimal(3,1),
    CONSTRAINT billitem_PK PRIMARY KEY (IDBillItem),
    CONSTRAINT billitem_FK1 FOREIGN KEY (IDItem) references Item(IDItem),
    CONSTRAINT billitem_FK2 FOREIGN KEY (IDBill) references bill(IDBill)
);


Insert into Ingredient values(1, 'Amstel draft', 'Beverage', 'l', 100);
Insert into Ingredient values(2, 'Espresso coffee', 'Beverage', 'pc', 275);
Insert into Ingredient values(3, 'Milk', 'Beverage', 'l', 90);

Insert into Item values(1, 'Amstel - small', 'Beverage', 'l', 5);
Insert into Item values(2, 'Amstel - large', 'Beverage', 'l', 7);
Insert into Item values(3, 'Espresso', 'Beverage', 'pc', 4.5);
Insert into Item values(4, 'Cappuccino', 'Beverage', 'pc', 5.5);

Insert into ItemContains(IDItem, IDIngredient,ContainsQuantity) values(1, 1, 0.3);
Insert into ItemContains(IDItem, IDIngredient,ContainsQuantity) values(2, 1, 0.5);
Insert into ItemContains(IDItem, IDIngredient,ContainsQuantity) values(3, 2, 1);
Insert into ItemContains(IDItem, IDIngredient,ContainsQuantity) values(4, 2, 1);
Insert into ItemContains(IDItem, IDIngredient,ContainsQuantity) values(4, 3, 0.05);

insert into Bill values(1, '12-jun-2013', null);

Insert into BillItem(IDBillItem, IDBill, IDItem, BillItemQuantity) values(1, 1, 1, 3);
Insert into BillItem(IDBillItem, IDBill, IDItem, BillItemQuantity) values(2, 1, 2, 1);
Insert into BillItem(IDBillItem, IDBill, IDItem, BillItemQuantity) values(3, 1, 3, 1);
Insert into BillItem(IDBillItem, IDBill, IDItem, BillItemQuantity) values(4, 1, 4, 2);


CREATE VIEW ItemsIngredientsView  AS 
  select sa.IDItem as "ID Item",  art.Name as "Item name", sa.ContainsQuantity as "Quantity", s.Unit as "Unit", s.IDIngredient as "ID Ingredient", s.Name as "Ingredient name" from ItemContains sa left join Ingredient s on sa.IDIngredient=s.IDIngredient left join Item art on art.IDItem = sa.IDItem;

CREATE VIEW BillView AS 
  select r.IDBill as "Bill", sr.IDItem as "ID Item", art.Name as "Item Name", sr.BillItemQuantity as "Quantity", art.Price as "Price" from Bill r right join BillItem sr on r.IDBill=sr.IDBill left join Item art on sr.IDItem = art.IDItem;

CREATE VIEW BillTotalPriceView AS 
  select sum(sr.BillItemQuantity*art.Price) as "TotalPrice" from Bill r right join BillItem sr on r.IDBill=sr.IDBill left join Item art on sr.IDItem = art.IDItem;
4

1 回答 1

1

以下是您如何处理创建 SQL Server 所需的基于集合的触发器的粗略概述,而不是 Oracle 的逐行触发器:

  1. 使用inserteddeleted元表查找数量的变化(更新后和更新前的值,deleted如果是插入inserted则为空,如果是删除则为空)。
  2. 对每个 的更改求和IngredientID
  3. 将包含所需调整总和的派生表加入到Ingredient表中并执行UPDATE.

使用您的示例架构(非常有帮助,谢谢——没有它我无法做到这一点),触发器如下所示:

CREATE TRIGGER TR_BillItem_UpdateQuantity
   ON dbo.BillItem FOR INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON;

UPDATE N
SET N.IngredientQuantity = N.IngredientQuantity - Change
FROM
   dbo.Ingredient N
   INNER JOIN (
      SELECT
         IC.IDIngredient,
         Change =
            Sum(
               (IsNull(I.BillItemQuantity, 0) - IsNull(D.BillItemQuantity, 0))
               * IC.ContainsQuantity
            )
      FROM
         Inserted I
         FULL JOIN Deleted D
            ON I.IDBillItem = D.IDBillItem
         INNER JOIN dbo.ItemContains IC
            ON IsNull(I.IDItem, D.IDItem) = IC.IDItem
      GROUP BY
         IC.IDIngredient
   ) S ON N.IDIngredient = S.IDIngredient
;

在 SQL Fiddle 上查看现场演示

根据 DML 操作,InsertedandDeleted表之一可以是空的,因此我们使用FULL JOIN它们,以便我们获得可用的 before 和 after 值。我们加入ItemContains表格以获取每种成分的数量,并对变化进行汇总,按成分分组,这样我们就可以建立一个总数来修改每种成分。当IsNull它是 aDELETE或 an时,部件会处理,INSERT因为在这些情况下,没有值被更改为,或者没有值被更改,但我们仍然必须正确调整。

您应该考虑一下如何处理取消或退款。小心任何数据操作,因为它会更新现有的成分数量。

于 2013-06-11T21:43:39.830 回答