我已经有点知道这个问题的答案了,但我总觉得在这个话题上我还需要了解更多。
我的基本理解是,一般而言,仅包含您可能在任何给定时间查询/排序的所有字段的单个索引不太可能有用,但我已经看到了这种类型的东西。比如,有人想,“好吧,如果我们只是把所有这些东西放在一个索引中,数据库就可以用它来找到它需要的东西”,而没有看到一些正在运行的实际查询的执行计划。
想象一下这样的表:
id int pk/uid
name varchar(50)
customerId int (foreign key)
dateCreated datetime
我可能会看到一个包含name
,customerId
和dateCreated
字段的索引。
但我的理解是,这样的索引不会在查询中使用,例如:
SELECT [id], [name], [customerId], [dateCreated]
FROM Representatives WHERE customerId=1
ORDER BY dateCreated
对于这样的查询,在我看来,一个更好的主意是包含customerId
和dateCreated
字段的索引,customerId
字段为“第一”。这将创建一个索引,该索引将以这样的方式组织数据,以便该查询可以快速找到它需要的内容 - 按照它需要的顺序。
我看到的另一件事,也许和第一件事一样频繁,是每个字段上的单独索引。name
因此,在customerId
和dateCreated
字段上各有一个。
与第一个示例不同,这种安排有时在我看来至少部分有用。查询的执行计划可能显示至少它使用 上的索引customerId
来选择记录,但它没有使用带有dateCreated
字段的索引来对它们进行排序。
我知道这是一个广泛的问题,因为对任何特定表集的任何特定查询的具体答案通常是查看执行计划说明它将做什么,否则将表和查询的细节纳入帐户。另外,我知道这取决于查询可能运行的频率,而不是为其维护特定索引的开销。
但我想我要问的是作为索引的一般“起点”,为特定的、频繁提取的查询和 WHERE 或 ORDER BY 子句中的字段设置特定索引的想法是否有意义?
您是对的,您的示例查询不会使用该索引。
如果出现以下情况,查询规划器将考虑使用索引:
它将无法使用以查询未使用的字段开头的索引。
所以对于你的例子:
它将考虑以下索引:
但不是:
如果它同时发现两者
[customerId]
,那么[customerId], [dateCreated], [name]
它是否更喜欢其中一个的决定将取决于索引统计数据,而索引统计数据取决于对字段中数据平衡的估计。如果[customerId], [dateCreated]
已定义,则它应该比其他两个更喜欢,除非您给出相反的特定索引提示。根据我的经验,为每个字段定义一个索引也很常见,尽管这很少是最佳的,因为在插入/更新时更新索引所需的额外管理以及存储它们所需的额外空间在一半时被浪费了它们可能永远不会被使用 - 但除非您的数据库看到写入繁重的负载,否则即使索引过多,性能也不会很糟糕。
频繁查询的特定索引通常会因表或索引扫描而变慢,这通常是一个好主意,但不要过度使用,因为您可能会将一个性能问题换成另一个问题。例如,如果您确实定义
[customerId], [dateCreated]
为索引,请记住查询计划器将能够将其用于将使用索引的查询([customerId]
如果存在)。虽然使用 just[customerId]
会比使用复合索引稍微更有效,但可以通过最终让两个索引而不是一个竞争 RAM 中的空间来缓解这种情况(尽管如果您的整个正常工作集很容易放入 RAM,那么这种额外的内存竞争可能不会一个问题)。要回答您最初的问题,是的,必须围绕查询设计索引,而不仅仅是表。索引中字段的顺序至关重要。设计单个索引以优化多个查询更加困难,您将不得不做出权衡。
关于您的第二点,是的,单个字段上的一堆索引非常常见。我一直在我的环境中看到它,这对我来说通常是一个危险信号,即开发团队没有与 DBA 合作设计适当的索引。
我设计索引的策略是索引:
所以对于你的例子:
我可能会在 (CustomerID, dateCreated) INCLUDE (id, name) 上设计一个索引。这个覆盖索引意味着查询不必访问原始表,从而大大提高了性能。
不过,这个例子几乎太简单了。仅 (CustomerID) 上的简单索引的性能几乎相同(假设每个客户只有一个代表,因此只需要对表进行一次书签查找)。根据对表运行的其他查询,实际对 (CustomerID, ID)执行聚集索引甚至可能是有益的。