3个常见的外键错误(以及如何避免它们)

530 阅读6分钟

外键是任何关系型数据库的一个重要元素。但是,当你设置你的数据库模式时,如果外键设置得不正确,就很容易给自己带来问题。

下面是外键最常见的三个错误,以及如何避免这些错误的一些建议。

1.数据类型不匹配

当你使用外键来引用另一个表中的列时,两个表的数据类型必须相同。例如,如果你要声明外键的引用列使用INT 数据类型,那么被引用的列也必须是INT

如果你很幸运,你会很容易诊断出你犯了这个错误。当你试图创建表时,你可能会得到一个错误信息,如Foreign key mismatchCould not create constraint

然而,根据你所使用的数据库系统和具体的数据库模式,很容易错过或误诊外键数据类型不匹配。

以SQLite数据库为例,有可能在外键数据类型不匹配的情况下成功创建一个表。这种错配可能会进一步造成其他错误。

避免这个问题的最简单的方法是,当你首先创建表时,确保所有用外键相互链接的列共享相同的数据类型。

如果你用一个新的外键约束来改变一个现有的表,如果你试图创建一个外键约束来连接不同数据类型的列,你的数据库系统可能会返回一个错误。

2.悬空的外键

一个悬空的外键是一个链接到一个不存在的表或列的外键。这显然是不好的做法,但它发生了 - 主要是因为有可能意外地创建悬空的外键。

在一些数据库管理系统中,你可以创建一个悬空的外键,只需先创建引用表,并包括一个外键约束,将其链接到你计划创建的下一个表中的一个列。如果你后来忘了创建那个被引用的表或列,你就有了一个悬空的外键。

许多数据库系统,包括CockroachDB,通过创建一个引用不存在的表或列的表来防止这种情况的发生--如果你尝试,CREATE TABLE 语句将以错误的方式失败,例如ERROR: relation "referenced_table" does not exist

更常见的是,当你删除一个在数据库中其他地方被引用的表或列时,悬空的外键会出现。一些数据库管理系统会允许这样做,留下一个悬空的外键,而另一些则会出现错误。例如,在CockroachDB中,试图删除一个被其他表的外键引用的表会导致以下错误:ERROR: "table_1" is referenced by foreign key from table "table_2"

其他数据库系统可能会抛出一些更难解析的错误。如果你试图删除一个列或表,但反复得到一个错误,这可能是因为你试图删除另一个表中的外键约束的东西。

避免悬空外键的最好方法是使用一个现代数据库系统,当约束被添加到一个表中时,它可以验证约束,并且不允许用户通过删除另一个表所需要的数据来破坏数据库的参考完整性。

如果升级到像CockroachDB这样的数据库是不可能的,那么防止外键悬空的下一个最好的办法就是用好的文档来强调外键关系,并且仔细计划,特别是在从数据库中删除任何东西的时候。

3.当你添加外键时没有创建外键索引

这个 "错误 "与我们谈到的其他两个有点不同。没有为外键创建索引不会产生错误信息。然而,不使用外键索引可能意味着你在无意中受到了很大的性能打击,特别是当你在大规模运行时。

外键索引可以大大改善涉及父表和子表之间连接的查询性能。你的应用程序执行这些连接的频率会有所不同,但在大多数情况下,创建一个外键索引是有必要的,因为它将使这些查询避免触发全表扫描。(全表扫描很慢,成本很高,最好避免)。

这里需要记住的是,当你指定一个表的主键时,主键索引会自动创建,而外键索引通常必须手动创建。

不要跳过这一步!即使你使用的是一个足够小的数据库,以至于性能的提高还不能带来有意义的变化,最好也要把索引建立起来。否则,随着你的规模和增长,在你的小型测试数据库上几毫秒的差异可能会变成你的用户在大规模的生产数据库上强烈感受到的滞后。

最大的外键错误是没有使用外键,而是依赖于数据库之间的非正式链接。

一个简单的例子可以说明问题:设想我们的销售数据库有一个leads (个人)的表和一个单独的companies

我们可以通过在某处的字符串中包含每个潜在客户的公司名称来非正式地连接这两个表,例如,作为列job_title 的一部分,该列的值可能是’sales rep at Veridian Dynamics’ 。我们可以将这两个表连接起来,例如,运行一个查询,检查companies 中的每个公司名称,看这个名称是否作为子串存在于leads.job_title 的任何行中。

这种方法效率很低,但更重要的是,它使错误很容易潜入我们的数据和随后使用的所有数据。使用外键约束可以帮助我们确保只有能与被引用表相联系的数据才能被输入我们的数据库中。

想象一下,继续我们的例子,一个用户不小心把一个线索的公司名称输入为Vridian Dynamics 。如果我们创建了一个leads.company 列,并有外键约束将其链接到companies.name ,那么用户的查询将立即返回一个错误,他们很可能会注意到并纠正他们的打字错误。如果我们只是使用前面描述的非正式链接,这个错别字将被输入数据库,而随后对在Veridian Dynamics工作的线索的查询会因为这个错别字而错过新的线索。

关键的启示:在你创建表之前要考虑清楚

归根结底,大多数常见的外键错误都是计划不周的结果。花一点额外的时间来规划你的数据库模式,可以帮助你避免以后的麻烦。

在计划阶段要问自己的好问题包括。

  • 这个表/列需要与哪些表相连?
  • 如果这个表/列被删除,会发生什么?
  • 这将如何扩展?

开始构建之前,花更多的时间考虑架构和你的工作将如何扩展,总是有好处的。