Oracle Triggers Update, основанный на строках заказа, выполнил, как избежать ошибки мутации
Для типовой базы данных продуктов и доставки я изучаю лучший способ запуска триггера, который:
- Когда строка заказа установлена на "Завершено", запускается триггер, который:
- Ищет любые другие строки заказа для этого заказа.
- Если все остальные строки заказа для этого заказа также "завершены"
- Обновите таблицу заголовка заказа, чтобы завершить.
Для раздела: в таблице заголовка заказа будет храниться общая сумма заказа, а в таблице 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