我记得听过Joel Spolsky在播客014中提到他几乎没用过外键(如果我没记错的话).但是,对我而言,在整个数据库中避免重复和后续数据完整性问题似乎非常重要.
人们有一些坚实的理由为什么(避免与Stack Overflow原则一致的讨论)?
编辑: "我还没有理由创建一个外键,所以这可能是我实际设置一个外键的第一个原因."
使用外键的原因:
你不会得到孤儿行
你可以得到很好的"删除级联"行为,自动清理表
了解数据库中表之间的关系有助于优化器计划您的查询以实现最高效的执行,因为它能够更好地估计连接基数.
FK对数据库中最重要的统计数据给出了相当大的暗示,这反过来又会带来更好的性能
它们支持各种自动生成的支持 - ORM可以生成自己,可视化工具将能够为您创建漂亮的模式布局等.
项目的新手将更快地进入事物流,因为其他隐式关系是明确记录的
不使用外键的原因:
你正在使每个CRUD操作的DB工作更多,因为它必须检查FK一致性.如果你有很多流失,这可能是一个很大的代价
通过强制执行关系,FK指定了必须添加/删除内容的顺序,这可能导致数据库拒绝执行您想要的操作.(当然,在这种情况下,你要做的是创建一个孤立行,这通常不是一件好事).当您进行大批量更新时,这是非常痛苦的,并且您在另一个表之前加载一个表,第二个表创建一致状态(但如果第二个加载可能失败并且您的数据库现在不一致?).
有时你事先知道你的数据会变脏,你接受了,并且你希望数据库接受它
你只是在偷懒:-)
我认为(我不确定!)大多数已建立的数据库提供了一种指定未强制执行的外键的方法,并且只是一些元数据.由于非执法部门消除了不使用FK的所有理由,如果第二部分中的任何原因适用,您应该走这条路线.
这是一个培养问题.如果你在教育或职业生涯的某个地方花时间喂养和照顾数据库(或者与有才能的人一起密切合作),那么实体和关系的基本原则在你的思维过程中根深蒂固.其中的基础是如何/何时/为什么在数据库中指定密钥(主要,外部和备用).这是第二天性.
但是,如果您在过去与RDBMS相关的工作中没有那么彻底或积极的经验,那么您可能没有接触到这些信息.或者也许你的过去包括沉浸在一个大声反数据库的环境中(例如,"那些DBA是白痴 - 我们很少,我们选择了几个java/c#代码甩尾者会挽救这一天"),在这种情况下你可能会强烈反对对于一些dweeb的神秘唠叨告诉你,如果你只是倾听,FK(以及他们可以暗示的限制)真的很重要.
大多数人都是在他们还是小孩的时候被教过,刷牙很重要.你可以没有它吗?当然,但是如果你在每顿饭后刷过,那么在某个地方你可以获得的牙齿比你可以拥有的牙齿少.如果妈妈和爸爸有足够的责任来涵盖数据库设计和口腔卫生,我们就不会进行这种对话.:-)
引用Joe Celko:
"就像26号皮带一样,只是因为你的意思并不代表你应该这么做!"
我确信有很多应用程序可以让你逃脱它,但这不是最好的主意.您不能总是指望您的应用程序正确管理您的数据库,坦率地管理数据库不应该是您的应用程序非常关注.
如果您使用的是关系数据库,那么您似乎应该在其中定义一些关系.不幸的是,这种态度(你不需要外键)似乎被许多应用程序开发人员所接受,他们宁愿不被数据完整性等愚蠢的事情所困扰(但需要因为他们的公司没有专门的数据库开发人员).通常在这些类型的数据库中,你很幸运只有主键;)
外键对于任何关系数据库模型都是必不可少的.
我总是使用它们,但后来我为金融系统制作数据库.数据库是应用程序的关键部分.如果财务数据库中的数据不完全准确,那么您在代码/前端设计中花费了多少精力并不重要.你只是在浪费时间.
还有一个事实是,多个系统通常需要直接与数据库接口 - 从刚读取数据的其他系统(Crystal Reports)到插入数据的系统(不一定使用我设计的API;它可能由一个刚刚发现VBScript且具有SQL框SA密码的愚蠢的经理.如果数据库不像它可能的那样白痴,那么再见数据库.
如果您的数据很重要,那么请使用外键,创建一组存储过程来与数据交互,并创建最难的数据库.如果您的数据不重要,为什么要开始使用数据库?
假设您正在使用外键.您正在编写一个自动化测试,上面写着"当我更新金融帐户时,它应该保存交易记录." 在这个测试中,你只关心两个表:accounts
和transactions
.
但是,accounts
有一个外键contracts
,并且contracts
有一个fk clients
,并且clients
有一个fk cities
,并且cities
有一个fk到states
.
现在,如果没有在与测试无关的四个表中设置数据,数据库将不允许您运行测试.
至少有两种可能的观点:
"这是一件好事:你的测试应该是现实的,那些数据限制将存在于生产中."
"这是一件坏事:你应该能够在不涉及其他部分的情况下对系统进行单元测试.你可以为系统整体添加集成测试."
也可以在运行测试时暂时关闭外键检查.MySQL至少支持这一点.
更新:我现在总是使用外键.我对"他们复杂测试"的反对意见的回答是"编写你的单元测试,以便他们根本不需要数据库.任何使用数据库的测试都应该正确使用它,包括外键.如果设置很痛苦,找到一种不太痛苦的方法进行设置."
"他们可以删除记录更加繁琐 - 你不能删除"主"记录,其他表中有外键会违反该约束的记录."
重要的是要记住,SQL标准定义了删除或更新外键时所采取的操作.我所知道的是:
ON DELETE RESTRICT
- 防止删除另一个表中具有此列中的键的任何行.这就是Ken Ray上面描述的内容.
ON DELETE CASCADE
- 如果删除了另一个表中的行,请删除此表中引用它的所有行.
ON DELETE SET DEFAULT
- 如果删除了另一个表中的某一行,请将引用它的任何外键设置为该列的默认值.
ON DELETE SET NULL
- 如果删除了另一个表中的行,请将此表中引用它的任何外键设置为null.
ON DELETE NO ACTION
- 这个外键只标记它是外键; 即用于OR映射器.
这些相同的行动也适用于ON UPDATE
.
默认值似乎取决于您使用的是哪个sql server.
@imphasing - 这正是那种导致维护噩梦的心态.
为什么哦,为什么你会忽略声明性参照完整性,其中数据可以保证至少是一致的,有利于所谓的"软件执行",这是一种最好的弱预防措施.
有一个很好的理由不使用它们: 如果你不理解它们的作用或如何使用它们.
在错误的情况下,外键约束可能导致事故的瀑布复制.如果有人删除了错误的记录,撤消它可能会成为一项巨大的任务.
而且,相反,当你需要移除某些东西时,如果设计不当,约束会导致各种阻止你的锁.
没有充分的理由不使用它们......除非孤立的行对你来说不是什么大问题.