外键是否能提高查询性能?

180

假设我有两个表,Products和ProductCategories。这两个表都以CategoryId为关系。以下是查询语句:

SELECT p.ProductId, p.Name, c.CategoryId, c.Name AS Category
FROM Products p
INNER JOIN ProductCategories c ON p.CategoryId = c.CategoryId
WHERE c.CategoryId = 1;

当我创建执行计划时,表ProductCategories执行集群索引搜索,这是预期的。但是对于表Products,它执行了集群索引扫描,这让我感到怀疑。为什么外键没有帮助提高查询性能?

因此,我不得不在Products.CategoryId上创建索引。当我再次创建执行计划时,两个表都执行索引搜索。并且估计的子树成本大大降低了。

我的问题是:

  1. 除了FK对关系约束有所帮助外,它是否还具有其他用处?它是否可以提高查询性能?

  2. 我应该在所有表中的所有FK列(例如Products.CategoryId)上创建索引吗?


我们真的需要看到两个表的规范才能给出全面的评论。两个表都需要在CategoryId上添加某种索引(主键/次键/外键)。而该列的数据类型非常关键。 - undefined
10个回答

217

外键是一种参照完整性工具,而不是性能工具。至少在 SQL Server 中,创建 FK 不会创建关联的索引,您应该在所有 FK 字段上创建索引以提高查找时间。


41
好的模型通常表现更优。 - Kenny Evitt
10
“Foreign Keys是一种关系完整性工具”--请谨慎使用“关系型”这个词。Foreign Keys是数据库概念,是参照完整性约束的简写形式。它们不是关系模型的一部分。我假设您打错了字。 - onedaywhen
8
有时候更好的模型会更贵,这是一个例子:外键会导致更多的处理发生,而不是减少。 - Hans
8
在MySQL中,外键确实能够提高性能。此外,你是正确的,创建外键并不会自动创建索引;创建外键必须要求先创建一个索引。 - Félix Adriyel Gagnon-Grenier
22
这个回答并没有回答问题,基本上是无用的。了解外键不打算对性能产生(积极的)影响很好,但问题关注的是现实情况,而不是意图。 - John
显示剩余4条评论

71

外键有助于(也可能损害)性能

  1. 据此处所述:外键可以提高性能

  2. 您应该始终在FK列上创建索引以减少查找。SQL Server不会自动执行此操作。

编辑

由于链接现在似乎无法访问(感谢克里斯注意到这一点),因此以下内容显示了外键为什么可以改善(也可能损害)性能的主要原因。

外键是否可以提高性能

在读取数据时,外键约束可提高性能,但在插入/修改/删除数据时会降低性能。

对于查询,优化器可以使用外键约束来创建更有效的查询计划,因为外键约束是预先声明的规则。这通常涉及跳过查询计划的某些部分,例如优化器可以看到由于外键约束,无需执行该特定部分的计划。


3
这是一个详细列举了降低性能的方法的链接: http://www.devx.com/getHelpOn/10MinuteSolution/16595/0/page/2 - cmsjr
3
这很有道理,但你只会在大量删除语句时遇到这个问题。也许结论应该是,在OLAP环境中,无索引的外键可以提高性能,而在OLTP环境中,它会降低性能。 - Lieven Keersmaekers
2
这个答案中的链接已经失效了。这很不幸,因为它是支持外键提高性能的唯一论据。 - Chris Moschini
1
@ChrisMoschini - 直到现在我才注意到你的评论。正如你所提到的,链接已经失效,但我发布了一个新的链接(附有详细信息),其中提到了主要内容。 - Lieven Keersmaekers
2
时光机链接大获成功!该文章也可以在SQLMag.com上找到,这里 - John Eisbrener
显示剩余3条评论

19

外键是DBMS(数据库管理系统)中确保数据库完整性的概念。

任何性能影响/改进都将特定于所使用的数据库技术,并且是外键目的的次要问题。

在SQL Server中,确保所有外键至少有一个非聚集索引是良好的实践。

我希望这能为您解决疑惑,但请随时要求更多详细信息。


11
如果你没有诚信,你的数据就毫无用处。我发现这种说法很容易被接受。 - HLGEM
@HLGEM 偶尔出现 404 错误 仍然可以接受。在使用更便宜的资源和不那么复杂的系统时获得卓越的吞吐量,这才是真正容易出售的。你可能会对 C.A.P. 定理 感兴趣。 - Daniel Dinnyes
9
@Daniel Dinnyes,数据完整性不是关于获得404错误。它关乎可用的数据。例如,由于开发人员的无能而导致订单和财务数据丢失,无法生成报告。没有使用外键是没有任何借口的。 - HLGEM
如果404错误不是为了简化而牺牲数据完整性(即没有外键),那我就不知道还有什么原因了。在某些极端情况下,强制执行引用完整性检查是一种负担,是无法承受的。 - Daniel Dinnyes
2
我同意HLGEM的观点。让你的代码处理完整性并不总是一个好主意。数据经常被用来做决策,但如果数据被损坏,那么决策将不准确。 - lepe
1
"外键是一种关系完整性工具。外键是数据库概念,是引用完整性约束的简写。它们不是关系模型的一部分。我假设您打错了字。" - onedaywhen

4

您在提高性能时最好使用经常使用的字段上的索引。如果您使用SQL Server,则可以使用分析器来分析特定数据库,并获取输出文件并使用调整向导以获得有关索引放置位置的建议。我还喜欢使用分析器来查找运行时间长的存储过程,我每周都会发布一个最糟糕的前十名名单,这可以保持人们的诚实:D。


3

您可以使用它来帮助提高查询效率。它允许您重构SQL Server中的查询,使用外连接代替内连接,这样就不需要再检查列中是否有空值了。您不需要添加限定符,因为外键关系已经为您执行了这项操作。

因此,如下所示:

select p.ProductId, p.Name, c.CategoryId, c.Name AS Category 
from Products p
inner join ProductCategories c on
p.CategoryId = c.CategoryId
where c.CategoryId = 1;

变成这样:

 SELECT p.ProductId, p.Name, c.CategoryId, c.Name AS Category 
 FROM ProductCategories c 
 LEFT OUTER JOIN Products P ON
 c.CategoryId = p.CategoryId 
 WHERE c.CategoryId = 1;

在小查询中,这不一定会产生巨大的性能提升,但当表变得很大时,它可能更加高效。


6
外连接通常比内连接效率低(https://dev59.com/yHE85IYBdhLWcg3wikEu#2726683)。现在你的查询也会产生歧义:你依赖于数据库将外连接隐式转换为内连接(以恢复性能),而不是明确地进行外连接和内连接的区分。 - Mark Sowul

3

我不太了解SQL Server,但在Oracle的情况下,拥有外键列会降低数据加载的性能。因为数据库需要针对每个插入操作检查数据完整性。是的,正如已经提到的那样,给外键列创建索引是一个好习惯。


3
在表中添加一个外键不会提高性能。简单地说,如果您要在ProductCategories表中插入记录,则数据库将尝试查找外键列是否具有存在于products表主键值中的值。每次在ProductCategories表中添加新条目时,这种查找操作都会给您的数据库带来额外的负担。 因此,添加外键不会提高数据库性能,但它可以维护数据库的完整性。如果您使用外键检查完整性而不是在程序中运行多个查询以检查记录是否存在于数据库中,则可以提高数据库的性能。

3
对于MySQL 5.7,它绝对可以惊人地加速涉及多个连接的查询!我使用“explain”来理解我的查询,并发现我正在连接4-5个表 - 根本没有使用任何键。我什么也没做,只是在这些表中添加了外键,结果负载时间减少了90%。以前需要> 5秒的查询现在只需要500毫秒或更少的时间。
这是一个巨大的改进!
而且,正如其他人提到的那样,您还会获得确保关系完整性的额外好处。此外,确保引用完整性也有其自己的性能好处。它具有第二阶效应,即确保具有外键的表与外部表“更新”。假设您有一个用户表和一个注释表,并且您正在对注释表进行某些统计。如果您硬删除用户,则可能也不再需要其评论。

1
在添加它们之前,这些表是否具有生成外键所需的索引? - George

0

0

是的,外键可以加速SELECT,但会减慢INSERT/UPDATE/DELETE

SQL Server使用所有约束(包括FK)来构建更好的SELECT执行计划。

例如,如果您运行一个查询,其中Column1 = X,而X不符合约束条件,则服务器甚至不会触及表格。

P.S. 除非该约束处于"不受信任"状态,但那是完全不同的故事。

P.P.S. 然而,拥有外键(或其他约束)可能会减慢INSERT/UPDATE/DELETE,除非您在此列上有一个非聚集索引。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接