在 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 语句中使用子查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 之 子查询与嵌套查询

在 Oracle 更新语句中使用子查询而不是表名

选择语句中的子查询如何在 oracle 中工作

Oracle-查询,,..

Oracle with重用子查询

oracle子查询