Reputation: 1090
I am trying to create a trigger that updates another table with PL/SQL and I am having some problems. (I have read this but doesn't help a lot).
Here is my situation, I have lets say 2 tables :
Customers Table
CustomerID number Primary Key, ItemsDelivered number
Items Table
CustomerID number, ItemID number, ItemDelivered Varchar(15)
Lets say that when somenone places an order we have a new record at Items table that looks like this:
| CustomerID | ItemID | ItemDelivered |
| 1 | 1 | False |
I want a trigger that will raise the ItemsDelivered counter whenever someone updates the ItemDelivered collumn to "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;
Here is my problem, if only the ItemDelivered column is updated there is no New.CustomerID!
Is there any way to get the CustomerID of the row that have just updated? (I have tried to join with inserted virtual table but I am getting an error that the table doesn't exists)
Upvotes: 0
Views: 12343
Reputation: 231651
In a row-level trigger on an UPDATE
, both :new.customerID
and :old.customerID
should be defined. And unless you're updating the CustomerID
, the two will have the same value. Given that, it sounds like you want
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;
That being said, however, storing this sort of counter and maintaining it with a trigger is generally a problematic way to design a data model. It violates basic normalization and it potentially leads to all sorts of race conditions. For example, if you code the trigger the way you were showing initially where you do a SELECT
to get the original count and then do an update, you'll introduce bugs in a multi-user environment because someone else could also be in the process of marking an item delivered and neither transaction would see the other session's changes and your counter would get set to the wrong value. And even if you implement bug-free code, you've got to introduce a serialization mechanism (in this case the row-level lock on the CUSTOMERS
table taken out by the UPDATE
) that causes different sessions to have to wait on each other-- that is going to limit the scalability and performance of your application.
To demonstrate that the :old.customerID
and the :new.customerID
will both be defined and will both be equal
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.
Upvotes: 3
Reputation: 1244
If you would like to store the item count in the database, I would recommend a pair of triggers. You would use an after row trigger to record the item number (perhaps in a table variable in your package) and an after statement trigger that will actually update the counter, calculating the items delivered directly from the base date. That is, by
select sum(itemsDelivered) from Customers where itemId = :itemId;
This way, you avoid the dangers of corrupting the counters, because you are always setting it to what it should be. It's probably a Good Idea to keep the derived data in a separate table.
We built our old system entirely on database triggers which updated data in separate "Derived" tables, and it worked very well. It had the advantage that all of our data manipulation could be performed by inserting, updating and deleting the database tables with no need to know the business rules. For instance, to put a student into a class, you just had to insert a row into the registration table; after your select statement, tuition, fees, financial aid, and everything else were already calculated.
Upvotes: 1