2012-04-06 70 views
0

我想创建一个触发器,用PL/SQL更新另一个表,我遇到了一些问题。 (我读过this,但没有太大帮助)。Oracle PL/SQL触发器更新另一列

这里是我的情况,我可以说,2个表:

客户

客户ID号主键,ItemsDelivered

项目

客户ID号,项ID号,ItemDelivered VARCHAR(15)

比方说,当somenone下订单,我们在项目表看起来像这样的新纪录:

| CustomerID | ItemID | ItemDelivered | 
|   1 | 1 | False  | 

我想要一个触发器,当有人更新ItemDelivered collum时将引发ItemsDelivered计数器n改为“True”。

create or replace Trigger UpdateDelivered 
    After Update On Items For 
    Each Row 
Declare 
    Counter Customers.ItemsDelivered%Type; 
Begin 
    If (:Old.ItemDelivered ='False' And :New.ItemDelivered='True') Then 
    Select ItemsDelivered into Counter From Customers where CustomerdID =:New.CustomerID; 
    Update.... 
    end if; 
END; 

这是我的问题,如果只有ItemDelivered列更新没有New.CustomerID!

有什么办法可以获得刚刚更新的行的CustomerID? (我曾尝试与插入虚拟表加入,但我得到的表不存在错误)上UPDATE

+2

Old.CustomerID是你在找什么,我想。但你真的想这样做吗?只需加入并计数,当你需要计数。最终,你将开始尝试维护像“ItemsDelivered”这样的计算列的完整性问题。 – Glenn 2012-04-06 15:46:57

+1

@VGeorge:请详细说明你想做什么,为什么你想要:New.customerID?和我thnk你想要的:OLD.customerID? – 2012-04-06 15:47:05

+0

非常感谢您的帮助!旧参数像魅力一样工作!我的情况与我所描述的有所不同,我简化了一下。我想要做的事情实际上是提高一个人的工资,每当从“未婚”到“已婚”。感谢真的很多,我真的很感谢帮助:) – VGe0rge 2012-04-06 16:01:49

回答

3

在行级触发器,既:new.customerID:old.customerID应该被定义。除非您更新CustomerID,否则两者的价值相同。鉴于此,这听起来像你想

create or replace Trigger UpdateDelivered 
    After Update On Items For 
    Each Row 
Begin 
    If (:Old.ItemDelivered ='False' And :New.ItemDelivered='True') Then 
    Update Customers 
     set itemsDelivered = itemsDelivered + 1 
     where customerID = :new.customerID; 
    end if; 
END; 

话虽这么说,但是,存储这种计数器和一个触发维护它通常是设计一个数据模型有问题的途径。它违反了基本的标准化,并且可能导致各种竞争条件。例如,如果您按最初显示的方式对触发器进行编码,以获取原始计数并进行更新,则会在多用户环境中引入错误,因为其他人也可能正在进行此过程标记交付的物品,并且这两个交易都不会看到其他会话的更改,并且您的计数器将设置为错误的值。即使你实现了无错代码,你也必须引入一个序列化机制(在这种情况下,UPDATECUSTOMERS表上的行级锁),这会导致不同的会话必须相互等待, - 这会限制应用程序的可伸缩性和性能。

为了证明:old.customerID:new.customerID都将被定义,都将是平等的

SQL> desc items 
Name          Null? Type 
----------------------------------------- -------- ---------------------------- 
CUSTOMERID           NUMBER 
ITEMID            NUMBER 
ITEMDELIVERED          VARCHAR2(10) 


SQL> ed 
Wrote file afiedt.buf 

    1 create or replace 
    2 trigger updateDelivered 
    3 after update on items 
    4 for each row 
    5 begin 
    6 if(:old.itemDelivered = 'False' and :new.itemDelivered = 'True') 
    7 then 
    8  dbms_output.put_line('New CustoerID = ' || :new.customerID); 
    9  dbms_output.put_line('Old CustomerID = ' || :old.customerID); 
10 end if; 
11* end; 
SQL>/

Trigger created. 

SQL> select * from items; 

CUSTOMERID  ITEMID ITEMDELIVE 
---------- ---------- ---------- 
     1   1 False 

SQL> update items 
    2  set itemDelivered = 'True' 
    3 where customerID = 1; 
New CustoerID = 1 
Old CustomerID = 1 

1 row updated. 
+0

非常感谢您的时间!我认为新老将有相同的价值,但他们没有!当我只更新ItemDelivered列时,new.customerID没有任何价值!我测试过了,并且没有工作!我同意你关于柜台的事情,我不会把它用于柜台,这是一个简单的例子。祝你有个美好的时光:) – VGe0rge 2012-04-07 08:17:58

+0

@ VGe0rge - 我更新了我的答案,演示了':old.customerID'和':new.customerID'都将被定义,并且当你更新'itemDelivered'柱。 – 2012-04-07 17:04:33

1

如果你想存储的项目数在数据库中,我会建议对触发器。您可以使用after row触发器记录项目编号(可能位于包中的表变量中)和after语句触发器,它们将实际更新计数器,计算从基准日期直接传送的项目。也就是说,通过

select sum(itemsDelivered) from Customers where itemId = :itemId; 

这样,你避免损坏柜台的危险,因为你总是将其设置为它应该是什么。将派生数据保存在单独的表中可能是一个好主意。

我们完全基于数据库触发器构建了旧系统,这些数据库触发器更新了单独的“派生”表中的数据,并且工作得非常好。它的优点是,我们所有的数据操作都可以通过插入,更新和删除数据库表来执行,而无需知道业务规则。例如,为了让一个学生进入一个班级,你只需要在注册表中插入一行;在你选择的声明之后,学费,费用,财政援助和其他一切都已经计算好了。

+0

这是一个更好的解决方案,我必须说!当时我实际上不需要一个计数器,但我会在任何时候想要一个计数器。感谢您的时间:) – VGe0rge 2012-04-07 08:19:09

+0

请注意,您有这种方法的种族条件相同。如果有两个会话将大致同时向两个独立客户交付的相同物品进行标记,则两个交易将不会彼此看到,您可以轻松地存储与详细信息不同的摘要信息。您可以使用快速刷新的物化视图,刷新提交时存储总是与细节数据同步的摘要信息。 – 2012-04-08 23:25:11

+0

其实没有。关键是你总是从头开始计算派生值,而不是从当前值开始计算。这样他们总是与基础数据保持一致。将派生数据保留在从未人为更新的单独表中也可以最大限度地减少锁定问题。 – 2012-04-10 20:04:19