1

我正在为大学课程学习 SQL 和 DB 设计。给我们的一项任务是创建一个具有派生属性的表,该派生属性是一些子属性的总和。例如:

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

ITEMS
itemNo {PK}
orderID {FK}
itemTotal

现在,我什至不确定这是一个好习惯。从我在网上阅读的一些内容来看,派生值不应存储,而应由用户应用程序计算。我可以理解这种观点,但在这种情况下,我的任务是存储派生值,更重要的是通过触发器保持它们的完整性,这对我来说相对较新,所以我喜欢使用它们。我还想象在一些更复杂的情况下,存储派生值确实值得节省处理时间。以下是我采取的没有给我带来问题的保障措施:

插入新子项时更新父项 /orderTotal 的触发器。

删除子项时更新父项 /orderTotal 的触发器。

当子 itemTotal 被修改时更新父 /orderTotal 的触发器。

但是,我想要另一种保障措施,但我不知道如何完成。由于父属性 /orderTotal 是派生的,因此永远不应手动修改它。如果有人确实尝试手动修改它(到一个实际上不是正确 SUM 的错误值),我想(a)阻止他们这样做或(b)一旦他们完成就将其恢复为旧值.

哪种方法更好,哪种方法可行(以及如何)?我不确定如何完成前者,我试图通过触发器或约束来完成后者,但似乎都不合适。触发器方法不断给我 ORA-04091 错误,因为我试图改变触发触发器的表。我认为约束方法也不合适,因为我不确定如何在约束检查中做这样的特定事情。

顺便说一句,我在 SQL Developer 中使用 Oracle SQL。

谢谢!

4

2 回答 2

3

“现在,我什至不确定这是一个好的做法。”

您的直觉是正确的:这不好的做法。出于某种原因,许多大学教授给他们的学生设置了编写糟糕代码的任务;如果他们至少解释说这是不好的做法并且永远不应该在现实世界中使用,这不会那么糟糕。但我想大多数教授对现实世界中重要的事情的了解有限。 叹息

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 - 说“变异表”。当我们尝试编写一个从拥有该触发器的表中进行选择的触发器时,就会发生这种情况。它几乎总是指向一个糟糕的数据模型,一个没有充分标准化的模型。这显然是这里的情况。

鉴于您被数据模型所困,唯一的解决方法是使用多个触发器和一个包的笨重实现。互联网提供了各种略有不同的解决方案:这里有一个.

于 2011-11-06T21:52:44.383 回答
0

一种解决方案可能是将用于维护的所有逻辑移动orderTotal到表上的INSTEAD OF UPDATE触发器ORDERS中。

您已经拥有的触发器ITEMS可以简化为更新ORDERS而无需进行计算 - 设置orderTotal to 0 or something like that. TheINSTEAD OF` 触发器将代替更新语句运行。

如果尝试手动更新订单总额,INSTEAD OF触发器将触发并重新计算现有值。

PS - 我没有要测试的 Oracle DB,但据我所知,INSTEAD OF触发器将绕过 ORA-04091 错误 - 如果这是错误的,请致歉

编辑 虽然此解决方案适用于其他一些 RDBMS 系统,但 Oracle 仅支持INSTEAD OF视图触发器。

编辑 2 如果分配允许,视图可能是解决此问题的合适方法,因为这将使订单值列能够被计算。

于 2011-11-06T07:40:38.717 回答