有一些开发人员不推荐使用完整性约束,你可能听过以下这么几点不使用外键的原因。
1、数据更新有可能和约束冲突。
2、当前的数据库设计如此灵活,以致于不支持引用完整性约束。
3、数据库为外键建立的索引会影响性能。
4、当前使用的数据库不支持外键。
5、定义外键的语法并不简单,还需要查阅。
一、反模式:无视约束
即使第一感觉告诉你,省略外键约束能使得数据库设计更加简单、灵活,或者执行更加高效,你还是不得不在其他方面付出相应的代价 -- 必须增加额外的代码来手动维护引用完整性。
1、完整性问题
很多人对引用完整性的解决方案是通过编写特定的程序代码来确保数据间的关系的。每次插入新记录时,需要确保外键列所引用的值在其对应的表中存在;每次删除记录时,需要确保所有相关的表都要同时合理地更新。
为了避免在没有外键约束的情况下产生引用的不完整状态,需要在任何改变生效前执行额外的select查询,以此来确保这些改变不会导致引用错误。
比如,在插入一条记录前之前,需要检查对应的被引用记录是否存在:
select Account_Id from Account where Account_Id = 1
然后才能执行Insert操作。
要删除一条记录,需要先确认没有别的记录引用了该条记录:
select bug_id from Bugs where reported_by = 1
然后才能执行Delete操作。
如果发生一个万一情况,这个Account_Id恰巧在删除操作的查询和删除语句的执行间隙插入了一条新的缺陷记录,这该怎么办?
2、级联问题
另外很多开发人员避免使用外键约束的理由,是因为这些约束会使得更新多张表中相关联的列变得比较麻烦。比如删除一条被其他记录依赖的数据时,不得不删除所有的子记录来避免违反外键约束:
delete from Account where RoleId = 1
delete from Role RoleId = 1
另外还有update
update Role set RoleId = 2
update Account set Role = 2 where RoleId = 1
说白了就是级联更新与级联删除有麻烦,因此干脆不用外键。
如果有人说的话,听起来像下面的意思,那么他们可能使用了本文所描述的反模式。
1、我要怎么写这个查询语句来检查一个值是否没有同时在两张表中存在。
通常这样的需要是为了查找那些孤立的行,那些被遗忘的数据记录行。
2、有没有一种简单的方法来判断在一张表中存在的数据是否也在第二张表中存在?
这么做是用来确认父记录确实存在,外键会自动完成这些,并且外键会使用附表的索引尽可能搞笑地完成。
3、外键?有人告诉我别用它,因为那会影响数据库的效率。
性能总是用来裁剪设计的一个很好的理由,但总是会引入更多的问题,甚至包括性能问题本身。我之前就看到过,说增加外键 增删改的速度会变慢的说法。但请记住,这种说法只适用于这张表是用于大规模增删改的情况。对于一张主要就是用来大范围增删改的表,不用外键似乎无可厚非。但是如果一张表,增删改并不多,这种说法就不成立了。相反,你避免了外键,却不得不使用更多的手段去维护数据完整性。
另外,大多数数据库都提供了级联更新与级联删除的功能。这个功能相信初学者都有所耳闻了。实际上,之前本文写代码的时,即使使用了级联更新与删除,但都是不放心,仍然在程序中,按照没有级联的情况,先执行一遍。对于此,我只想对自己说,既然使用了级联功能,就相信它。如果你不相信它,还要自己写代码级联。那就别用它。而事实上,我发现数据库的级联功能,比自己写代码维护要可靠得多。
对于开销方面,的确对增删改需要那么一点额外的系统开销,但是同时另一方面赚会来的有:
1、不需要在更新或删除记录前执行select进行检查。
2、在同步修改时不需要再锁住整张表。
3、不再需要执行定期的监控脚本来修正不可避免的垃圾数据,孤立数据。
外键是一个使用很方便的东西,提高性能,还能帮助你在任何简单或复杂的形式的数据变更下始终维持引用完整性。
总结:客观的分析,本人还是建议使用外键。