该不该用外键?

Posted by zhangxiaojian on October 31, 2014

作业第二弹,讲了四节课的经验型软件工程,要求随便找一个问题,用经验型软件工程的办法研究一下。于是想到实习的时候,老大经常说,现在在项目中基本上不用数据库的外键了。因为改起来太麻烦,费事。就借此复习一下外键内容。此处略去为了交作业强加上去的经验型软件工程流程。

外键简介:

参照完整性

我们常常希望保证一个关系中给定属性集上的取值也在另一个关系的某一属性集的取值中出现,这一条件称为参照完整性。

外键

外键 (FK) 是用于建立和加强两个表数据之间的链接的一列或多列。通过将保存表中主键值的一列或多列添加到另一个表中,可创建两个表之间的链接。这个列就成为第二个表的外键。

举个例子(此例来自数据库系统概念一书):

假设银行账户的表account如下(加粗为主键):

**account_number** branch_name balance

另一张表示具体分行信息的表branch表如下:

**branch_name** branch_city assets

上述两张表中,branch_name属性列即作为account中的列,也作为branch中的主键。表明了两张表的关联关系。这种情况下,我们就可以在数据库定义时指定branch_name属性是account表参考branch表的外键。

具体的SQL定义如下:

    create table branch
    	( branch_name		char(15),
              branch_city		char(30),
              assets		numeric(16,2),
              primary key (branch_name),
              check(assets >= 0))
    
    create table accout
    	( accout_number		char(10),
              branch_name		char(15),
              balance		numeric(12,2),
              primary key (account_number),
              foreign key (branch_name) references branch,
              check(balance >= 0)) 
    

外键的作用

外键的主要作用就是保证数据的参照完整性。假设上述例子有两个元组的值为:

**1** East_lake_whu 1203.00
**East_lake_whu** Whuhan 10000000.00

如果有一天东湖支行被取缔了,那么就要在表branch中删除上面这行。删除之后,查找账户1的开户支行就找不到了,岂不是成了黑户?这种情况下,就违反了参照完整性。

当违反参照完整性约束时,通常的处理是拒绝执行导致完整性被破坏的操作(即进行更新操作的事务被回滚)。所以当上述删除操作执行时,由于accout中设置了外键,在branch中删除支行的时候,就会去参考了它的表中查找,看看是否存在参考East_lake_whu的accout。如果存在,删除就会被拒绝,因为违反了参照完整性。

对于上述问题,如果确实需要删除此支行,那么比较安全的办法是首先在表account中把和East_lake_whu相关的账户都删掉,然后再删除支行信息。但是这样给数据库使用带来麻烦。如果不清楚外键是怎么设计的,或者被多个表设为外键,那么处理起来会很麻烦。

数据库提供了另一种可选择和配置的处理方式。在创建表account中添加配置信息如下:

create table account
(.
 foreign key (branch_name) references branch
 on delete cascade on update cascade,   
 )

配置之后,在遇到branch中元组被删除的情况,为了保证参照完整性,就会级联的删除account表中所有相关的元组。这样显得有些“暴力”。试想东湖支行被取缔之后,在在这里开户的所有账户都被删除了。那这银行估计要倒闭才行。

分析猜测:

综上所述,添加了外键有利于保证数据的参照完整性,但是同时又增加了数据库使用的难度。并且在数据比较庞大的情况下,在DBMS中不断验证参照完整性,会大大降低数据库的响应速度。关于外键使用好与坏的讨论在互联网上曾出现过,比如这个很火帖子:http://www.itpub.net/thread-1312844-1-1.html

个人意见:

工业使用和理论研究往往有出入,应具体问题具体分析。个人觉得数据库如果在响应速度和数据规范完整上面抉择。还是偏向响应速度一方。关于数据库外键所维护的参照完整性,可以在应用程序层面根据实际情况多做一些校验。给数据库减压。据我实习时了解的情况,公司在大中型软件开发中,基本上已经摒弃了外键,将需要的校验放在使用层了。