在 Oracle 的 Check 语句中使用子查询
Posted
技术标签:
【中文标题】在 Oracle 的 Check 语句中使用子查询【英文标题】:Using subquery in a Check statement in Oracle 【发布时间】:2010-11-01 16:44:01 【问题描述】:所以我试图解决这个问题,但似乎最后一行(检查)不允许其中的子查询。有什么方法可以使这项工作成为 Oracle 吗?
CREATE TABLE Tank (
n_id int,
day date,
level int,
CONSTRAINT pk_w_td PRIMARY KEY (n_id,day),
CONSTRAINT fk_w_td_tan FOREIGN KEY (n_id) REFERENCES Tanks ON DELETE CASCADE,
CHECK (level > 0 AND level <= (SELECT capacity FROM Tanks WHERE Tanks.n_id = TanksDay.n_id))
);
这是错误信息:
Error at Command Line:7 Column:32 Error report: SQL Error: ORA-02251: subquery not allowed here
02251. 00000 - "subquery not allowed here"
*Cause: Subquery is not allowed here in the statement.
*Action: Remove the subquery from the statement.
【问题讨论】:
很好的问题。一般的跨表约束(除了 FK 约束)是我最希望看到添加到 Oracle 中的特性之一。 【参考方案1】:由于 CHECK 约束不能基于查询,因此有三种基本方法可以解决此类问题。
选项 1:触发器
最简单的方法是在 TANK 上放置一个触发器,以查询 TANKS 并在 LEVEL 超过 CAPACITY 时抛出异常。然而,这种简单方法的问题在于,几乎不可能正确处理并发问题。如果会话 1 降低 CAPACITY,则会话 2 增加 LEVEL,然后两个事务都提交,触发器将无法检测到违规。如果很少修改其中一个或两个表,这可能不是问题,但总的来说,这将是一个问题。
选项 2:物化视图
您可以通过创建连接 TANK 和 TANKS 表的 ON COMMIT 物化视图,然后在物化视图上创建验证 LEVEL
选项 3:更改数据模型
如果您在表 A 中有一个值依赖于表 B 中的限制,这可能表明 B 中的限制应该是表 A 的属性(而不是表 B 的属性) .当然,这取决于您的数据模型的具体情况,但通常值得考虑。
【讨论】:
【参考方案2】:不幸的是,CHECK 约束不能包含子查询 - 请参阅 documentation。
【讨论】:
【参考方案3】:您正在寻找的功能称为 SQL 断言,and it's not yet implemented in Oracle 12c
【讨论】:
我同意,断言可以完美解决这种需求。【参考方案4】:贾斯汀的回答有一些好主意。另一种方法是用一个包(如果你愿意的话,一个 TAPI)来包装对表的所有插入/更新,并在那里实施检查。您需要确保所有应用程序都使用您的 TAPI。您还需要实现一些自定义锁定以保护约束免受并发活动的影响。
【讨论】:
【参考方案5】:如果超出允许范围,您可能需要创建触发器并使用RAISE_APPLICATION_ERROR
。
【讨论】:
创建触发器时要考虑的另一件事是,您可能希望在表保持容量上使用触发器,以确保它永远不会小于最大级别。以上是关于在 Oracle 的 Check 语句中使用子查询的主要内容,如果未能解决你的问题,请参考以下文章