我记得在播客014中听到Joel Spolsky提到他几乎从未使用过外键(如果我没记错的话)。然而,对我来说,它们对于避免数据库中的重复和后续数据完整性问题非常重要。
人们是否有一些可靠的理由(以避免与Stack Overflow原则一致的讨论)?
编辑:“我还没有创建外键的理由,所以这可能是我真正建立一个外键的第一个理由。”
我记得在播客014中听到Joel Spolsky提到他几乎从未使用过外键(如果我没记错的话)。然而,对我来说,它们对于避免数据库中的重复和后续数据完整性问题非常重要。
人们是否有一些可靠的理由(以避免与Stack Overflow原则一致的讨论)?
编辑:“我还没有创建外键的理由,所以这可能是我真正建立一个外键的第一个理由。”
当前回答
我只知道Oracle数据库,不知道其他数据库,而且我知道外键对于保持数据完整性是必不可少的。在插入数据之前,需要建立一个数据结构,并且建立正确的数据结构。当这一步完成时——所有的主键和外键都创建好了——工作就完成了!
意思是:孤立的行?不。这辈子都没见过。除非一个糟糕的程序员忘记了外键,或者他在另一个层次上实现了外键。在Oracle的环境中,这两者都是巨大的错误,会导致数据复制、孤儿数据,从而导致数据损坏。我无法想象一个没有强制FK的数据库。在我看来是一片混乱。这有点像Unix权限系统:假设每个人都是root用户。想想混乱吧。
外键是必不可少的,就像主键一样。这就像是说:如果我们移除主键会怎样?那么,整个混乱将会发生。这是什么。不能将主键或外键的职责移到编程级别,但必须移到数据级别。
缺点呢?是的,当然!因为在插入时,会有更多的检查。但是,如果数据完整性比性能更重要,那么这是显而易见的。Oracle上的性能问题更多地与索引有关,索引包含PK和FK。
其他回答
没有充分的理由不使用它们……除非孤行对你来说不是什么大问题。
数据结构设计的一个好的原则是确保表或对象的每个属性都服从于一个很好理解的约束。这很重要,因为如果您或您的程序可以依赖数据库中的有效数据,那么就不太可能出现由坏数据引起的程序缺陷。您还可以花费更少的时间来编写处理错误条件的代码,并且更有可能预先编写错误处理代码。
在许多情况下,这些约束可以在编译时定义,在这种情况下,您可以编写一个筛选器来确保属性总是在范围内,或者尝试保存属性失败。
然而,在许多情况下,这些约束可以在运行时更改。例如,您可能有一个“cars”表,其属性为“colour”,初始值为“red”、“green”和“blue”。在程序执行期间,可以将有效的颜色添加到初始列表中,并且添加的新“汽车”可以采用最新颜色列表中的任何颜色。此外,您通常希望这个更新的颜色列表在程序重新启动后仍然有效。
To answer your question, it turns out that if you have a requirement for data constraint that can change at run-time, and those changes must survive a program restart, foreign keys are the simplest and most concise solution to the problem. The development cost is the addition of one table (e.g. "colours", a foreign key constraint to the "cars" table, and an index), and the run-time cost is the extra table lookup for the up-to-date colours to validate the data, and this run-time cost is usually mitigated by indexing and caching.
如果您不使用外键来满足这些需求,那么您必须编写软件来管理列表、查找有效的条目、将其保存到磁盘、如果列表很大的话有效地组织数据、确保对列表的任何更新都不会破坏列表文件、在有多个读取器和/或写入器的情况下提供对列表的串行访问,等等。例如,你需要实现大量的RDBMS功能。
更大的问题是:你会戴着眼罩开车吗?如果你开发一个没有参考约束的系统就是这样。请记住,业务需求会改变,应用程序设计会改变,代码中相应的逻辑假设也会改变,逻辑本身也会被重构,等等。一般来说,数据库中的约束是在当代逻辑假设下放置的,对于特定的逻辑断言和假设集似乎是正确的。
在应用程序的整个生命周期中,引用检查和数据检查约束控制通过应用程序收集的数据,特别是当新需求驱动逻辑应用程序更改时。
从实时事务处理系统的角度来看,外键本身不会“提高性能”,也不会显著“降低性能”。然而,在大容量“批处理”系统中,约束检查存在一个聚合代价。这就是实时和批量事务处理的区别;批处理——通过约束检查,按顺序处理的批处理的总成本会对性能造成影响。
在一个设计良好的系统中,数据一致性检查将在处理批处理之前完成(然而,这里也有相关的成本);因此,加载时不需要外键约束检查。事实上,所有的约束,包括外键,都应该暂时禁用,直到批处理完成。
QUERY性能——如果表是在外键上连接的,那么要认识到外键列不是索引的事实(尽管根据定义,各自的主键是索引的)。通过索引一个外键,也就是索引任何键,在索引上连接表有助于提高性能,而不是通过连接一个带外键约束的非索引键。
Changing subjects, if a database is just supporting website display/rendering content/etc and recording clicks, then a database with full constraints on all tables is over kill for such purposes. Think about it. Most websites don’t even use a database for such. For similar requirements, where data is just being recorded and not referenced per say, use an in-memory database, which does not have constraints. This doesn’t mean that there is no data model, yes logical model, but no physical data model.
我不得不在这里第二多的评论,外键是必要的项目,以确保你有完整的数据。ON DELETE和ON UPDATE的不同选项将允许你绕过一些人们在这里提到的关于它们的使用的“下降”。
我发现在我99%的项目中,我会使用FK来加强数据的完整性,然而,在很少的情况下,我的客户必须保留他们的旧数据,不管它有多糟糕....但后来我花了很多时间写代码,只得到有效的数据,所以它变得毫无意义。
在我参与的一个项目中,经常存在隐式关系而不是显式关系,这样可以在同一列上连接多个表。
请看下面的表格
地址
AddressId (PK) EntityId EntityType 城市 状态 国家 等。
EntityType的值可能是Employee、Company、Customer, EntityId指的是您感兴趣的表的主键。
我真的不认为这是最好的方法,但它对这个项目有效。