0

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

这是我的情况,我可以说两张桌子:

客户

CustomerID号 主键,ItemsDelivered

项目

CustomerID编号、ItemID编号、ItemDelivered Varchar(15)

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

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

我想要一个触发器,只要有人将 ItemDelivered 列更新为“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?(我试图加入插入的虚拟表,但我收到表不存在的错误)

4

2 回答 2

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) 导致不同的会话必须相互等待——这将限制应用程序的可伸缩性和性能。

证明 the:old.customerID和 the: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.
于 2012-04-06T17:19:24.480 回答
1

如果您想将项目计数存储在数据库中,我会推荐一对触发器。您将使用后行触发器来记录项目编号(可能在包裹中的表变量中)和后语句触发器,该触发器将实际更新计数器,计算直接从基准日期交付的项目。也就是说,由

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

这样,您就避免了损坏计数器的危险,因为您总是将其设置为应有的值。将派生数据保存在单独的表中可能是一个好主意。

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

于 2012-04-07T01:01:04.693 回答