1

for a typical products & shipping Database I am exploring the best way to run a trigger that:

  1. When an order line is set to 'Complete', a trigger is ran that:
  2. Looks for any other order lines for that order.
  3. If all other order lines for that order are also 'Complete'
  4. Update the order header table to complete.

For clatiry: The order header table would store the overall oder total, and the orderLines table stores each product of the order.

SO far, the trigger is written as such:

CREATE OR REPLACE TRIGGER orderComplete
after update ON orderline
for each row
WHEN (new.orderline_fulfilled = 'Y')
DECLARE count NUMBER := 5;
ordersNotDone NUMBER;
BEGIN

SELECT COUNT(Orderline_fulfilled) INTO ordersNotDone
FROM orderHeader
JOIN orderline ON
orderHeader.Order_id = orderLine.Orderline_order
WHERE Order_id = :old.orderline_order
AND orderline_fulfilled = 'Y';

IF ordersNotDone = 0
THEN
UPDATE orderHeader
SET completed = SYSDATE
    WHERE orderId = :old.orderline_order;
ENDIF;

END;

This above causes the mutation error, when updating the orderline row.

user885983
  • 458
  • 4
  • 9
  • Please ignore my answer. As @JustinCave pointed out it would cause a mutating table error. If you want to unaccept it just click the green check-mark again. – Ben Apr 28 '12 at 23:06

3 Answers3

3

Enforcing integrity with a trigger is inherently problematic because the RDBMS read consistency mode allows multiple changes simultaneously that cannot see each others' result.

A better solution might be to avoid denormalising the data, and rely on detecting the presence of an incomplete order line to identify incomplete orders. As this would be the minority of cases it can be optimised with a function-based index along the lines of:

create index my_index on orderline(case orderline_complete when 'NO' then orderid else null end)

This will index only the values of orderline where orderline_complete is 'NO', so if there are only 100 such rows in the table then the index will only contain 100 entries.

Identifying incomplete orders is then a matter only of a full or fast full index scan of a very compact index with a query:

select distinct
  case orderline_complete when 'NO' then orderid else null end orderid
from
  orderline
where
  case orderline_complete when 'NO' then orderid else null end is not null;
David Aldridge
  • 51,479
  • 8
  • 68
  • 96
  • I am experiencing issues with above answer causing table mutation errors when updating an orderline to shipped. I presume this is exactly the kind of problem(s) you are referring to? Currently I'm troubleshooting to negate away from the table mutation error – user885983 Apr 28 '12 at 22:31
  • 2
    Not only that. Trigger-based checks generally require explicit locking or a single user environment to be successful. – David Aldridge Apr 28 '12 at 22:52
  • Thank you for your advice, in the interest of simplicity, I am looking for the Correct way of a simple trigger in general terms, fires when all rows of a related result set equal 'Y'. I do not disregard your wise advice, however, I am looking to identify the correct logic for a trigger. I'm Thinking a Statement-Level trigger with the PL/SQL block checking for completed orders. – user885983 Apr 29 '12 at 17:57
  • Hmmm, unfortunately you're looking for a correct way to implement a flawed methodology – David Aldridge Apr 30 '12 at 07:53
0

The simplest answer is to use a slightly different type of trigger which is triggered not after a row is updated but after the table is updated. This will not suffer from this problem.

So do something like:

CREATE or REPLACE TRIGGER trigger_name
AFTER INSERT ON orderline --note no for each row
BEGIN

  --loop over all orders which contain no unfulfilled orders
  FOR lrec IN (SELECT order_id FROM orderline group by order_id where not exists (select 1 from orderline where orderline_fulfilled = 'Y'))
  LOOP
    -- do stuff to order id because this are complete
  END LOOP;
END;

So here we may have completed multiple orders on the insert so the trigger needs to cope with this. Sorry I do not have an oracle instance to play with at home. Hope this helps

Kara
  • 6,115
  • 16
  • 50
  • 57
john
  • 141
  • 4
0

If you are using 11g then check about compound triggers, example: http://www.toadworld.com/KNOWLEDGE/KnowledgeXpertforOracle/tabid/648/TopicID/TRGC1/Default.aspx

Jokke Heikkilä
  • 918
  • 1
  • 8
  • 17