在Oracle的Check语句中使用子查询

dev*_*ium 21 sql database oracle

所以我试图解决这个问题,但似乎最后一行(检查)不允许子查询.有没有办法让这个工作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))
);
Run Code Online (Sandbox Code Playgroud)

这是错误信息:

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.
Run Code Online (Sandbox Code Playgroud)

Jus*_*ave 24

有三种基本方法可以解决此类问题,因为CHECK约束不能基于查询.

选项1:触发器

最简单的方法是在TANK上设置触发器,查询TANKS并在LEVEL超过CAPACITY时抛出异常.但是,这种简单方法的问题在于,几乎不可能正确处理并发问题.如果会话1降低了CAPACITY,则会话2增加LEVEL,然后两个事务提交,触发器将无法检测到违规.如果一个或两个表很少被修改,这可能不是问题,但一般来说这将是一个问题.

选项2:物化视图

您可以通过创建连接TANK和TANKS表的ON COMMIT物化视图,然后在物化视图上创建CHECK约束来验证LEVEL <= CAPACITY来解决并发问题.您还可以通过使物化视图仅包含违反约束的数据来避免存储数据两次.这将需要两个基表上的物化视图日志,这将为插入添加一些开销(尽管少于使用触发器).将检查推送到提交时将解决并发问题,但它引入了一些异常管理问题,因为COMMIT操作现在可能因为物化视图刷新失败而失败.您的应用程序需要能够处理该问题并提醒用户注意这一事实.

选项3:更改数据模型

如果表A中的值取决于表B中的限制,则可能表示B中的限制应该是表A的属性(而不是表B的属性,或者除了作为表B的属性之外).当然,这取决于您的数据模型的具体情况,但它通常值得考虑.


Ton*_*ews 7

不幸的是,CHECK约束不能包含子查询 - 请参阅文档.