Oracle PL / SQL触发器更新另一列

时间:2012-04-06 15:29:00

标签: sql oracle triggers

我正在尝试创建一个用PL / SQL更新另一个表的触发器,我遇到了一些问题。 (我读过this但没有多少帮助)。

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

客户

  

CustomerID 编号主键, ItemsDelivered 编号

项目

  

CustomerID 号码, ItemID 号码, ItemDelivered Varchar(15)

让我们说当有人下订单时,我们在Items表中有一条新记录,如下所示:

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

我想要一个触发器,只要有人将ItemDelivered collumn更新为“True”,就会引发ItemsDelivered计数器。

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? (我试图加入插入的虚拟表,但是我收到一个表不存在的错误)

2 个答案:

答案 0 :(得分:3)

UPDATE的行级触发器中,应定义: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;
然而,尽管如此,存储这种计数器并用触发器维护它通常是设计数据模型的有问题的方法。它违反了基本的标准化,并可能导致各种竞争条件。例如,如果您按照初始显示的方式对触发器进行编码SELECT以获取原始计数然后进行更新,则会在多用户环境中引入错误,因为其他人也可能在标记交付的项目的过程中,任何交易都不会看到其他会话的更改,并且您的计数器将被设置为错误的值。即使你实现了无错误的代码,你也必须引入一个序列化机制(在这种情况下,由CUSTOMERS取出的UPDATE表上的行级锁)导致不同的会话必须等待彼此 - 这将限制应用程序的可伸缩性和性能。

要证明: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.

答案 1 :(得分:1)

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

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

这样,您可以避免破坏计数器的危险,因为您始终将其设置为应有的危险。将派生数据保存在单独的表中可能是个好主意。

我们完全依靠数据库触发器构建我们的旧系统,这些触发器在单独的“派生”表中更新数据,并且它运行良好。它的优点是可以通过插入,更新和删除数据库表来执行所有数据操作,而无需了解业务规则。例如,要将学生放入课堂,您只需在注册表中插入一行;在你的选择陈述之后,学费,学费,经济资助以及其他一切都已经计算好了。

相关问题