Oracle Triggers Update, основанный на строках заказа, выполнил, как избежать ошибки мутации

Для типовой базы данных продуктов и доставки я изучаю лучший способ запуска триггера, который:

  1. Когда строка заказа установлена ​​на "Завершено", запускается триггер, который:
  2. Ищет любые другие строки заказа для этого заказа.
  3. Если все остальные строки заказа для этого заказа также "завершены"
  4. Обновите таблицу заголовка заказа, чтобы завершить.

Для раздела: в таблице заголовка заказа будет храниться общая сумма заказа, а в таблице orderLines хранится каждый продукт заказа.

Пока что триггер записан так:

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;

Это выше вызывает ошибку мутации при обновлении строки строки заказа.

3 ответа

Обеспечение целостности с помощью триггера по своей сути проблематично, потому что режим согласованности чтения СУБД допускает несколько изменений одновременно, которые не могут видеть результаты друг друга.

Лучшее решение может состоять в том, чтобы избежать денормализации данных и полагаться на обнаружение наличия неполной строки заказа для выявления неполных заказов. Поскольку это будет меньшинство случаев, его можно оптимизировать с помощью индекса на основе функций в следующем виде:

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

Это будет индексировать только значения строки заказа, где orderline_complete равен 'NO', поэтому, если в таблице только 100 таких строк, индекс будет содержать только 100 записей.

В этом случае для идентификации неполных заказов требуется только полное или быстрое сканирование полного индекса очень компактного индекса с запросом:

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;

Самый простой ответ - использовать несколько иной тип триггера, который срабатывает не после обновления строки, а после обновления таблицы. Это не пострадает от этой проблемы.

Так что сделайте что-то вроде:

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;

Таким образом, здесь мы могли выполнить несколько ордеров на вставке, поэтому триггер должен с этим справиться. Извините, у меня нет оракула, с которым можно играть дома. Надеюсь это поможет

Если вы используете 11g, проверьте составные триггеры, например: http://www.toadworld.com/KNOWLEDGE/KnowledgeXpertforOracle/tabid/648/TopicID/TRGC1/Default.aspx

Другие вопросы по тегам