Как предотвратить изменение производного значения (SUM) вручную до неверного значения - PullRequest
1 голос
/ 06 ноября 2011

Я изучаю дизайн SQL и DB для класса колледжа.Нам было дано одно назначение - создать таблицу с производным атрибутом, который является суммой некоторых дочерних атрибутов.Например:

ORDERS
orderID {PK}
/orderTotal    /* derived from SUM of child itemTotals */

ITEMS
itemNo {PK}
orderID {FK}
itemTotal

Теперь я даже не уверен, что это хорошая практика.Из некоторого чтения, которое я сделал в Интернете, производные значения не должны храниться, а должны вычисляться пользовательскими приложениями.Я могу понять эту перспективу, но в этом случае мое назначение - хранить производные значения и, что более важно, поддерживать их целостность с помощью триггеров, которые являются относительно новыми для меня, поэтому я наслаждаюсь их использованием.Я бы также подумал, что в некоторых более сложных случаях действительно стоит сэкономить время обработки для хранения производных значений.Вот меры предосторожности, которые я установил, которые НЕ доставляют мне проблем:

Триггер, который обновляет parent / orderTotal при вставке нового дочернего элемента.

Триггер, который обновляет parent / orderTotalпри удалении дочернего элемента.

Триггер, который обновляет parent / orderTotal при изменении дочернего itemTotal.

Однако есть другая защита, которую я хочу, и не могу понять, как ее выполнить.Поскольку родительский атрибут / orderTotal является производным, он никогда не должен изменяться вручную.Если кто-то попытается вручную изменить его (на ошибочное значение, которое на самом деле не является правильным SUM), я хочу либо (а) помешать ему сделать это, либо (б) вернуть его к старому значению, как только они будут сделаны.

Какой подход лучше, а какой возможен (и как)?Я не уверен, как выполнить первое, и я пытался выполнить второе с помощью триггера или ограничения, но ни один из них не показался мне подходящим.Метод триггера продолжал давать мне ошибку ORA-04091 за попытку изменить таблицу, которая сработала триггер.Метод ограничения, я не думаю, что он также подходит, так как я не уверен, как сделать такую ​​конкретную вещь в проверке ограничения.

Кстати, я использую Oracle SQL в SQL Developer.

Спасибо!

Ответы [ 2 ]

3 голосов
/ 07 ноября 2011

«Я даже не уверен, что это хорошая практика».

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

Anyhoo, чтобы ответить на ваш вопрос. Есть два подхода к этому. Можно было бы использовать триггер, чтобы «исправить», то есть проглотить изменение. Это было бы неправильно, потому что пользователь, пытающийся изменить значение, вероятно, потратил бы много времени, пытаясь выяснить, почему его изменение не прилипало, не осознавая, что он нарушает бизнес-правило. Так что гораздо лучше выкинуть исключение.

В этом примере используется синтаксис Oracle, потому что я предполагаю, что это то, что вы используете.

create or replace trigger order_header_trg
    before insert or update
    on order_header for each row
begin
    if :new.order_total != :old.order_total
    then
        raise_application_error 
                 ( -20000, 'You are not allowed to modify the value of ORDER_TOTAL');
    end if;
end;

Единственная проблема этого подхода заключается в том, что он не позволит вам вставить строки в ORDER_LINES и затем получить новый итог для ORDER_HEADER.

Это одна из причин, по которой денормализованные итоги являются плохой практикой.

Ошибка, которую вы получаете - ORA-04091 - говорит "таблица мутаций". Это происходит, когда мы пытаемся написать триггер, который выбирает из таблицы, которой принадлежит триггер. Это почти всегда указывает на плохую модель данных, которая недостаточно нормализована. Это, очевидно, случай здесь.

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

0 голосов
/ 06 ноября 2011

Одним из решений может быть перемещение всей логики для поддержки orderTotal в триггер INSTEAD OF UPDATE на таблице ORDERS.

Триггеры, которые у вас уже есть в ITEMS, можно упростить дообновить ORDERS без выполнения расчетов - вместо оператора update будет запущен триггер orderTotal to 0 or something like that. The INSTEAD OF`.

Если будет предпринята попытка вручную обновить общую сумму заказа, триггер INSTEAD OF будетзапустить и пересчитать существующее значение.

PS - у меня нет базы данных Oracle для тестирования, но, насколько я могу судить, триггер INSTEAD OF обойдет ошибку ORA-04091 -Приносим извинения, если это неправильно

РЕДАКТИРОВАТЬ Хотя это решение будет работать в некоторых других системах RDBMS, Oracle поддерживает только триггеры INSTEAD OF для представлений.

РЕДАКТИРОВАТЬ 2 Если назначение позволяет, представление может быть подходящим решением этой проблемы, поскольку это позволит рассчитать столбец значения заказа.

...