2011-11-06 84 views
1

我正在学习大学课程的SQL和DB设计。赋予我们的一项任务是创建一个具有派生属性的表格,这是一些子属性的SUM。例如:如何防止将派生值(SUM)手动更新为不正确的值

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

ITEMS 
itemNo {PK} 
orderID {FK} 
itemTotal 

现在,我甚至不确定这是好的做法。从我在网上完成的一些阅读中,派生值不应该被存储,而是由用户应用程序计算。我可以理解这种观点,但在这种情况下,我的任务是存储派生值,更重要的是通过触发器保持其完整性,这对我来说是比较新的,所以我很享受使用它们。我还想象一下,在一些更复杂的情况下,保存处理时间真的值得存储派生值。以下是我已经设置的不会给我带来问题的安全措施:

插入新子项时更新父/订单总计的触发器。

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

当修改child itemTotal时更新parent/orderTotal的触发器。

但是,还有另一个我想要的保障,我无法弄清楚如何完成。由于派生了父属性/ orderTotal,因此不应手动修改它。如果有人试图手动修改它(对于实际上不是正确的SUM的错误值),我想要(a)阻止他们这样做或(b)一旦完成就将其恢复到其旧值。

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

我在SQL Developer中使用Oracle SQL的方式。

谢谢!

+0

我会质疑在数据库中使用触发器来更新派生值之前,我会质疑存储派生值的适当性的适当性。为什么不让应用程序的业务逻辑直接设置该字段?这样,如果有特定地点的税收和/或运费等事情,则业务逻辑可以计算并存储正确的派生值,并将这些值考虑在内。 – aroth

+0

@aroth:他把它描述为一个DB类的赋值,所以没有选择在哪里做。 – jmoreno

回答

3

“现在,我甚至不能确定thise是很好的做法。”

你的直觉是对的:这个不好的做法。出于某种原因,许多大学教授将学生设置为编写不良代码的任务;如果他们至少说明这是不好的做法,并且不应该在现实世界中使用,那么这不会太糟糕。但是,我认为大多数教授对现实世界中的重要性掌握有限。 感叹

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

假设你被卡住的数据模型,唯一的解决方法是使用多个触发器和包笨重的实现。互联网提供了各种略有不同的解决方案:here is one

0

一个解决方案可能是将orderTotal的所有逻辑移入ORDERS表中的INSTEAD OF UPDATE触发器。

的触发你已经在ITEMS可以简化更新ORDERS而不进行计算 - 设置orderTotal to 0 or something like that. The INSTEAD OF`触发器将替代更新语句的运行。

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

PS - 我没有一个Oracle数据库来测试,但是从我所知道的,一个INSTEAD OF触发器会得到解决的ORA-04091错误 - aplologies如果这是错误的

编辑 虽然此解决方案可以在其他一些RDBMS系统中运行,但Oracle仅在视图上支持触发器INSTEAD OF

编辑2 如果分配允许的话,一个视图可以是一个合适的解决这个问题,因为这将使得能够计算出的顺序值列。

+0

感谢您的回复......我尝试了类似于自己的事情,但似乎INSTEAD OF只适用于视图而不适用于表格。但我不是专家,所以也许我做错了什么。 – The111

+0

@Johnson - Ahh - 对不起 - 在文档中错过了。您可以通过将'ORDERS'表作为一个视图并让最终用户访问该视图而不是使用Oracle安全性的基础表来解决这个问题吗? –

+0

@Johnson - 是否使用触发器?一个视图可能是一个使用触发器的更好的解决方案。 –