【问题标题】:Oracle Database with very few foreign key constraints具有很少外键约束的 Oracle 数据库
【发布时间】:2013-11-14 18:55:06
【问题描述】:

我刚刚开始了一个新项目,我遇到了一个只有 3 个外键约束的生产应用程序 Oracle 10g 数据库。我不习惯看到没有外键约束的数据库。我猜测不使用 FK 可能有一些性能/并发性考虑。原因是架构师在逻辑数据库模式中指定了所有关系,但这些关系并未在数据库中实现为外键约束。

问题:我读到我可以使用 RELY NOVALIDATE 定义一个不会影响性能的外键约束。是否值得在此数据库上定义 RELY FK 约束,以便可以轻松查看关系?这个应用程序不是使用 ORM 构建的,没有外键真的值得吗?

通过下面的示例对数据库进行非规范化

表 1 : FINProduct(ID (number), Description(varchar(5)), FINproductCode(varchar(10))...)

表 2:FINProductCode(ID (number, FINproductCode(varchar(10)) , LastUpdated(datetime)...)

因此,表 1 和表 2 之间没有关系,FINproductCode 列只是在表 1 中复制。

现在喝太早了,但我想我需要一个!

【问题讨论】:

  • 没有与 FK 相关的性能损失。或者换句话说,它们是如此之小,以至于获得的数据完整性更有价值。因此,如果可能,请尝试添加 FK。在此过程中,您可能会发现一些数据指向不存在的父行。这需要解决。可能是从 MySQL 移植的数据 DB 在其中一个引擎中不支持 FK。 dev.mysql.com/doc/refman/5.5/en/ansi-diff-foreign-keys.html "对于 InnoDB 以外的存储引擎,MySQL Server 解析 CREATE TABLE 语句中的 FOREIGN KEY 语法,但不使用或存储它。"
  • 在某些情况下,FK 的性能损失是不值得的——主要是在数据仓库环境中,通过完全控制加载过程来确保数据完整性。但是,在 OLTP 数据库中,您确实应该为每个“逻辑”外键关系添加一个“物理”外键约束。
  • @Frank Schmitt:是的,你是对的。在执行大量 INSERTS 和 UPDATES FK 时,可能会出现性能问题。
  • 据我了解,这个应用程序是一个 OLTP 数据库,而不是数据集市/仓库。在我从震惊中恢复过来后,我意识到这个数据库严重非规范化,因此缺少 FK。对于 OLTP 类型的应用程序进行这种程度的非规范化真的有用吗?此 OLTP 主要用于金融交易

标签: oracle foreign-key-relationship


【解决方案1】:

我会非常谨慎地假设没有外键约束是对性能问题的合理回应。强制执行外键约束会产生开销(尤其是在缺少适当索引的情况下),但您的应用程序不太可能比 Oracle 更有效地验证约束。所以问题实际上是您是否想要外键约束的小开销,或者几乎可以肯定您会将无效数据插入数据库。这不太可能是您想要做出的权衡 - 我还没有遇到一个愿意捕获不正确和难以理解的数据的业务用户,即使这样做比捕获正确数据要快一点.

除非有更多的背景资料,否则我倾向于创建所有缺失的外键约束。创建RELY NOVALIDATE 约束是可能的,但它破坏了外键约束的主要好处——首先防止无效数据进入数据库。

【讨论】:

  • 这有一个推论。如果 FK 约束在数据输入数据库的表上,并且没有 FK 的表是由代码单独构建的;那么如果正确完成,则可能不需要 FK,因为已知数据存在。这显然只适用于数据永远不会被删除的情况。
  • 据我了解,此应用是 OLTP 数据库,而不是数据集市/仓库。在我从震惊中恢复过来后,我意识到这个数据库严重非规范化,因此缺少 FK。对于 OLTP 类型的应用程序进行这种程度的非规范化真的有用吗?此 OLTP 主要用于金融交易
  • @alwaysshungry - 不。如果您正在记录金融交易,您真的非常希望交易被正确记录。如果您对数据进行非规范化,您几乎可以保证交易记录将不正确,因为每次非规范化属性更改时,没有人会正确更新历史记录。如果系统缺少外键,你几乎可以保证你会有孤立和无效的行。
  • FK 也可以对性能产生积极影响。在某些情况下,它们帮助 Oracle 生成有效的执行计划。如果没有 FK,您可能会面临执行计划稳定性的问题。
【解决方案2】:

这取决于您是要添加 FK 仅用于文档目的,还是要防止将来使用无效的 FK 值进行 INSERT/UPDATE。

如果您只希望它用于文档目的,我会使用 RELY NOVALIDATE 创建 FK 约束并在之后禁用它 - 否则,Oracle 将检查它是否有未来的 INSERT / UPDATE。

但是:除非您绝对需要,否则不要这样做!

我同意 Justin Cave 的观点:在大多数情况下,您应该只添加“普通”FK 约束 - 这样,您可以确保现有数据是正确的。

【讨论】:

    【解决方案3】:

    我会尝试创建约束并将违规报告到异常表中。修复数据并启用约束。

    创建一些测试数据

    create table parent (pk integer
                        ,data varchar2(1)
                        ,CONSTRAINT PARENT_PK PRIMARY KEY (PK)  ENABLE );
    
    create table child (pk integer
                        ,pk_parent integer
                        ,data varchar2(1)
                        ,CONSTRAINT CHILD_PK PRIMARY KEY (PK)  ENABLE );
    
    insert into parent values (1,'a');
    insert into parent values (2,'b');
    
    
    insert into child values (1,1,'a');
    insert into child values (2,2,'b');
    insert into child values (3,3,'c');
    

    创建外键约束:

    alter table child add constraint fk_parent foreign key(pk_parent) references parent(pk);
    
    SQL Error: ORA-02298: Kan (ROB.FK_PARENT) niet valideren - bovenliggende sleutels zijn niet gevonden.
    02298. 00000 - "cannot validate (%s.%s) - parent keys not found"
    *Cause:    an alter table validating constraint failed because the table has
               child records.
    *Action:   Obvious
    

    使用“启用 novalidate”选项创建外键

    alter table child add constraint fk_parent foreign key(pk_parent) references parent(pk) enable novalidate;
    table CHILD altered.
    
    insert into child values (4,4,'c');
    
    SQL Error: ORA-02291: Integriteitsbeperking (ROB.FK_PARENT) is geschonden - bovenliggende sleutel is niet gevonden.
    02291. 00000 - "integrity constraint (%s.%s) violated - parent key not found"
    *Cause:    A foreign key value has no matching primary key value.
    *Action:   Delete the foreign key or add a matching primary key.
    

    不能插入违反 FK 的新数据。

    现在让我们修复表中已经存在的违反 FK 约束的数据

    创建异常表并尝试启用约束:

    create table exceptions(row_id rowid,
                            owner varchar2(30),
                            table_name varchar2(30),
                            constraint varchar2(30));
    
    
    ALTER TABLE child ENABLE constraint fk_parent  EXCEPTIONS INTO EXCEPTIONS; 
    
    
    Error report:
    SQL Error: ORA-02298: Kan (ROB.FK_PARENT) niet valideren - bovenliggende sleutels zijn niet gevonden.
    02298. 00000 - "cannot validate (%s.%s) - parent keys not found"
    *Cause:    an alter table validating constraint failed because the table has
               child records.
    *Action:   Obvious
    

    检查异常表是否有问题:

    select * from exceptions;
    
    ROW_ID OWNER                          TABLE_NAME                     CONSTRAINT                   
    ------ ------------------------------ ------------------------------ ------------------------------
    AABA78 ROB                            CHILD                          FK_PARENT                      
    AAFAAA                                                                                              
    Ow9AAC 
    
    select  * from child where rowid = 'AABA78AAFAAAOw9AAC';
    

    解决问题

    delete from child where pk = 3;
    
    1 rows deleted.
    
    ALTER TABLE child ENABLE constraint fk_parent  EXCEPTIONS INTO EXCEPTIONS;
    
    table CHILD altered.
    

    约束已启用且数据正确

    【讨论】:

    • 设计示例如下:
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 1970-01-01
    • 2021-11-03
    • 2014-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多