为了强制执行检查约束,跨表重复列是一个好主意吗?

时间:2010-02-05 16:18:36

标签: database oracle constraints

我遇到的情况是我有两张桌子:A和B. B有A的外键。

A有一个“详细”列,用于标识其B中的子女是否需要填写“详细信息”部分。

如果我有精益结构,那么我无法确定B中的记录是否需要填写“详细信息”部分,即不是空,而不是加入A.因此,对我来说唯一的方法是防止某人将这些记录插入或更新为无效状态是触发与A连接并检查其“详细”列。

我的感觉是约束比触发更好,因为它们更像是关于数据的事实,除了过滤器,而触发器只是过滤器。

我可以通过复制B中的“详细”列然后检查约束(详细='Y'和详细信息是非空)或(详细='N')

关于最佳方法的想法?

5 个答案:

答案 0 :(得分:3)

您提到的所有工具(约束和触发器)只是强制数据库中数据一致性的一种方式。

简单的业务规则,例如“总是有参考”,“没有NULL”等,可以通过约束强制执行。

应该使用触发器强制执行更复杂的业务规则,例如您在此提及的业务规则。

约束并不比触发器“更好”或“更差”:它们只是您需要经常实施的规则的捷径。

对于您的任务,只需实施一个触发器。

但是,在Oracle中,约束和触发器都不是以纯粹的基于集合的方式实现的。对于受DML操作影响的每条记录,它们都会循环调用。

最有效的方法是创建一个包,该包可作为针对您的表的所有DML的单个入口点,并检查该包中的Details

答案 1 :(得分:2)

你是正确的在数据库级别强制执行此操作,Quassnoi的要点都很好。此外,您可能希望调查让此操作的API引用两个表中的updatable join view并通过它实现约束。

答案 2 :(得分:2)

在理想的世界中,Oracle和其他DBMS将支持“断言”:

create assertion no_more_than_50_per_user as
check(not exists(select null
                 from a join b on ...
                 where a.detailed = 'Y'
                 and b.details is null
);

他们不是(并且没有充分的理由:以表现方式实施它们会非常困难!)

正如Quassnoi建议的那样,可以使用触发器 - 但您需要了解多用户环境中的危险。为了确保执行一致性,您需要在检查数据时取出锁定,以确保不会发生这种情况:

(假设记录1当前具有详细='N',但所有关联的B记录都具有非空的详细信息。)

user1> Update A set detailed = 'Y' where a_id=1;

这样可行,因为所有关联的B行的详细信息都不为空。

user2> Update B set details = null where a_id=1;

这样可行,因为user1还没有提交,所以user2的触发器看到详细='N'。

user1> commit;
user2> commit;

现在您的数据已损坏。为防止这种情况,B上的触发器需要选择A行“进行更新”。

答案 3 :(得分:0)

我会在UI中强制执行这样的规则。如果您的业务规则变得更加复杂,那么您将完全掌握模型中的大量冗余列,以便在数据库模式中强制执行所有规则。

答案 4 :(得分:0)

如果要在B上复制Detailed字段,您可以使用外键来强制执行它,例如(B.KEYFIELD,B.DETAILED)参考文献(A.KEYFIELD,A.DETAILED)。我并不是想在B上重复字段,但另一方面,似乎你有与B相关的数据存在于A.如果你可以从A中删除DETAILED并将其推到B,那么设计可能是清洁器。一些其他细节可能有所帮助。

分享并享受。