我是否需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

And*_*ber 22 index sql-server

我已经有点知道这个问题的答案了,但我总觉得我需要更多地了解这个话题。

我的基本理解是,一般来说,仅包含您可能在任何给定时间查询/排序的所有字段的单个索引不太可能有用,但我已经看到了这种类型的事情。就像有人想的那样,“好吧,如果我们把所有这些东西都放在一个索引中,数据库就可以使用它来找到它需要的东西”,而从未见过一些正在运行的实际查询的执行计划。

想象一个像这样的表:

id int pk/uid
name varchar(50)
customerId int (foreign key)
dateCreated datetime
Run Code Online (Sandbox Code Playgroud)

我可能会看到一个包含name,customerIddateCreated字段的索引。

但我的理解是这样的索引不会在查询中使用,例如:

SELECT [id], [name], [customerId], [dateCreated]
   FROM Representatives WHERE customerId=1 
   ORDER BY dateCreated
Run Code Online (Sandbox Code Playgroud)

对于这样的查询,在我看来,更好的主意是包含customerIddateCreated字段的索引,该customerId字段是“第一”。这将创建一个索引,该索引将以这样一种方式组织数据,以便该查询可以快速找到它需要的内容 - 按照它需要的顺序。

我看到的另一件事,也许和第一件事一样频繁,是每个字段的单独索引;所以,每一个上namecustomerIddateCreated领域。

与第一个例子不同,这种安排在我看来有时至少是部分有用的。查询的执行计划可能会显示至少它使用 上的索引customerId来选择记录,但它没有使用带有dateCreated字段的索引对它们进行排序。


我知道这是一个广泛的问题,因为对任何特定表集的任何特定查询的具体答案通常是查看执行计划所说的它将要做什么,否则将表和查询的细节纳入帐户。另外,我知道这取决于查询的运行频率,而不是为其维护特定索引的开销。

但我想我要问的是作为索引的一般“起点”,为特定的、经常提取的查询和 WHERE 或 ORDER BY 子句中的字段设置特定索引的想法有意义吗?

Dav*_*ett 27

您是对的,您的示例查询不会使用该索引。

如果出现以下情况,查询规划器将考虑使用索引:

  • 查询中引用了其中包含的所有字段
  • 一些从头开始的字段被引用

它将无法使用以查询未使用的字段开头的索引。

所以对于你的例子:

SELECT [id], [name], [customerId], [dateCreated]
   FROM Representatives WHERE customerId=1 
   ORDER BY dateCreated
Run Code Online (Sandbox Code Playgroud)

它将考虑以下索引:

[customerId]
[customerId], [dateCreated]
[customerId], [dateCreated], [name]
Run Code Online (Sandbox Code Playgroud)

但不是:

[name], [customerId], [dateCreated]
Run Code Online (Sandbox Code Playgroud)

如果它发现两者[customerId][customerId], [dateCreated], [name]决定选择一个而不是另一个将取决于索引统计数据,该统计数据取决于对字段中数据平衡的估计。如果[customerId], [dateCreated]被定义,它应该优先于其他两个,除非您给出相反的特定索引提示。

根据我的经验,为每个字段定义一个索引的情况并不少见,尽管这很少是最佳的,因为在插入/更新时更新索引所需的额外管理以及存储它们所需的额外空间在一半时被浪费了它们可能永远不会被使用 - 但除非您的数据库看到写入繁重的负载,否则即使索引过多,性能也不会太差。

由于表或索引扫描而导致速度缓慢的频繁查询的特定索引通常是一个好主意,但不要过度使用它,因为您可能会将一个性能问题换成另一个。[customerId], [dateCreated]例如,如果您确实定义为索引,请记住,查询计划器将能够将其用于将使用索引的查询([customerId]如果存在)。虽然使用 just[customerId]会比使用复合索引稍微更有效,但这可以通过最终让两个索引而不是一个来竞争 RAM 中的空间来缓解(尽管如果您的整个正常工作集很容易地适合 RAM,那么额外的内存竞争可能不会一个问题)。


Bra*_*adC 6

要回答您的原始问题,是的,必须围绕查询设计索引,而不仅仅是。索引中字段的顺序非常重要。为多个查询设​​计最佳的单个索引比较困难,您将不得不进行权衡。

关于您的第二点,是的,单个字段上的一堆索引非常常见。我一直在我的环境中看到它,这对我来说通常是一个危险信号,即开发团队没有与 DBA 合作设计适当的索引。

我设计索引的策略是索引:

  • WHERE 中使用的字段(按选择性排序)
  • ORDER BY 中使用的字段
  • 包括其他字段(如有必要)以制作覆盖索引

所以对于你的例子:

SELECT [id], [name], [customerId], [dateCreated]
   FROM Representatives WHERE customerId=1 
   ORDER BY dateCreated
Run Code Online (Sandbox Code Playgroud)

我可能会在 (CustomerID, dateCreated) INCLUDE (id, name) 上设计一个索引。这种覆盖索引意味着查询不必访问原始表,从而极大地提高了性能。

不过,这个例子几乎简单了。仅 (CustomerID) 上的朴素索引的性能几乎相同(假设每个客户只有一个代表,因此只需要对表进行单个书签查找)。根据对表运行的其他查询,实际对 (CustomerID, ID)执行聚集索引甚至可能是有益的。