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


149

假设我有2个表格,产品和产品类别。两个表在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,它执行集群索引扫描,这使我感到怀疑。为什么FK不能帮助提高查询性能?

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

我的问题是:

  1. 除了FK有助于关系约束之外,它还有其他用途吗?它会提高查询性能吗?

  2. 是否应该在所有表的所有FK列(如Products.CategoryId)上创建索引?

Answers:


186

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


40
好的模型(通常)表现更好。
肯尼·埃维特

10
“外键是一种关系完整性工具”-请谨慎使用“关系”一词。外键是数据库概念,是引用完整性约束的简写。它们不是关系模型的一部分。我想你打错了。
2012年

7
@Kenny通常是,但是有时候更好的模型会花更多的钱。恰当的例子:外键使更多的处理发生,而不是更少。
汉斯

8
至少在MySQL中,外键确实可以提高性能。而且,您是对的,创建FK不会创建索引。一个FK的创建需要一个指数
费利克斯·加侬-格雷尼尔

15
这个答案几乎没有用,因为它不能回答问题。我们很高兴地知道,外键不打算对性能(正)的影响,但问题是关于现实,而不是意图。
约翰

58

外键可以改善(和伤害)性能

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

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

编辑

由于该链接现在似乎已失效(对Chris表示敬意),因此以下内容说明了外键可以改善(和损害)性能的原因。

外键能否提高性能

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

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


3
这是一个链接,详细介绍了它们降低性能的方式devx.com/getHelpOn/10MinuteSolution/16595/0/page/2
cmsjr,2009年

3
那是有道理的,但您只会在使用大量的delete语句时遇到此问题。可能的结论应该是,在OLAP环境中,未索引的FK将提高性能,而在OLTP环境中,它将降低性能。
Lieven Keersmaekers,2009年

1
该答案中的链接已死。这是不幸的,因为这是FK提高性能的唯一理由。
克里斯·莫斯基尼

1
@ChrisMoschini-直到现在我都没有注意到您的评论。正如您所提到的,该链接已失效,但是我发布的新链接(包含详细信息)中提到了其要点。
Lieven Keersmaekers 2012年

2
赢的Wayback Machine 链接!该文章也可以在SQLMag.com发现,在这里
John Eisbrener '16

15

外键是用于确保数据库完整性的DBMS概念。

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

在SQL Server中,优良作法是确保所有外键上至少都具有非聚集索引。

我希望这可以为您解决问题,但请随时索取更多详细信息。


9
@Kenny Evitt如果您没有完整性,则您的数据无用。我发现销售非常容易。
HLGEM 2011年

@HLGEM偶尔遇到404错误仍然是可以忍受的。使用较便宜的资源和较不复杂的系统来获得非凡的吞吐量,现在它也很容易销售。您可能对CAP定理感兴趣。
Daniel Dinnyes 2011年

8
@Daniel Dinnyes,数据完整性与获取404错误无关。这与拥有可用数据有关。例如,这是因为开发人员的能力不足,不会丢失报告的订单和财务数据。不使用外键没有任何借口。
HLGEM 2011年

2
我同意HLGEM。让代码处理完整性并非总是一个好主意。数据通常用于做出决策,但是如果数据已损坏,则决策将不准确。
lepe 2012年

1
“外键是一种关系完整性工具”-请谨慎使用“关系”一词。外键是数据库概念,是引用完整性约束的简写。它们不是关系模型的一部分。我想你打错了。
2012年

4

最好的性能选择是在经常使用的字段上使用索引。如果使用SQL Server,则可以使用事件探查器来探查特定的数据库,并获取输出的文件,并使用调整向导来接收有关放置索引的建议。我还喜欢使用探查器清除长期运行的存储过程,我每周都会发布前十名最严重的违规者列表,这使人们保持诚实:D。


3

您可以使用它来帮助提高查询效率。它确实允许您在SQL Server中重组查询,以使用外部联接而不是内部联接,这消除了sql server不必检查列中是否为null的必要性。您不需要放入该限定符,因为外键关系已经为您增强了该限定符。

所以这:

    select p.ProductId, p.Name, c.CategoryId, c.Name AS Category 
from Products p inner join ProductCategories c on p.CategoryId = c.CategoryIdwhere 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;

在小查询中,这不一定会带来巨大的性能,但是当表变大时,效率会更高。


3
不仅外部联接通常不如内部联接(stackoverflow.com/a/2726683/155892)效率低,而且现在您的查询具有误导性:您依靠数据库将外部联接隐式地转换为内部联接(恢复性能)。而不是明确地做
马克·索沃

2

对于MySQL 5.7,它无疑可以很好地加速涉及多个联接的查询!

我使用“解释”来理解我的查询,发现我正在加入4-5个表-根本没有使用任何键。除了添加外键到这些表外,我什么也没做,结果是加载时间减少了90%。现在,耗时大于5秒的查询花费的时间不超过500毫秒。

这是一个巨大的进步!

而且,正如其他人提到的那样,您将获得确保关系完整性的额外好处。

除此之外,确保引用完整性也具有其自身的性能优势。它具有确保具有外键的表与外表“保持最新”的二阶效果。假设您有一个用户表和一个注释表,并且正在对注释表进行一些统计。如果您硬删除用户,则可能也不再需要他们的评论。


在添加表之前,表是否具有生成外键所需的索引?
乔治

1

在表中添加外键不会提高性能,只是说如果您在ProductCategories表数据库中插入一条记录,则会尝试查找在产品表的主键值中存在的外键列,此查询如下:每次在ProductCategories表中添加新条目时,数据库上的操作都是开销。因此,添加外键不会提高数据库性能,但会照顾数据库的完整性。是的,如果您使用外键检查完整性,而不是运行许多查询来检查程序中数据库中是否存在记录,则它将提高db的性能。


0

我对SQL Server不太了解,但是对于Oracle,拥有外键列会降低数据加载的性能。这是因为数据库需要检查每个插入的数据完整性。是的,正如已经提到的,在外键列上建立索引是一种好习惯。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.