在SQL Server 2008中解释具有聚簇索引的大型表的最佳查询

时间:2012-12-05 10:50:12

标签: sql-server performance clustered-index sql-execution-plan

我正在处理一张非常大的桌子(每天大约增加270万行),其结构如下:

CREATE TABLE [dbo].[Result](
    [ResultDate] [date] NOT NULL,
    [Thing1Id] [int] NOT NULL,
    [Num] [int] NOT NULL,
    [Thing2Id] [int] NOT NULL,
CONSTRAINT [PK_Result] PRIMARY KEY CLUSTERED 
(
    [ResultDate] ASC,
    [Thing1Id] ASC,
    [Num] ASC
))

由于聚簇主键位于ResultDate,Thing1Id和Num上,我希望以下查询是最佳的:

SELECT Thing2.* 
FROM dbo.Result
INNER JOIN Thing2 ON Thing2.Id = result.Thing2Id
WHERE 
    ResultDate >= '2012-01-01'
    AND
    ResultDate <= '2012-01-30'
    AND Thing1Id = 23

正如您所看到的,查询是在1月12日为特定的Thing1找到结果。

但是,执行计划表明通过添加以下索引可以获得巨大的性能提升:

CREATE NONCLUSTERED INDEX [IX_Missing]
ON [dbo].[Result] ([Thing1Id],[ResultDate])
INCLUDE ([Num],[Thing2Id]) 

当然,添加此索引确实可以大幅提升性能。

有人可以解释一下原因吗?就我而言,应该使用聚簇主键充分缩小结果,添加它会使索引大小更大,并增加不必要的开销。

我能否以不同的方式索引表以获得更好的性能?

(请注意,实际上该表实际上是2个联合表,数据每天从一个表转移到另一个表,并且每月对数据进行分区。)

3 个答案:

答案 0 :(得分:0)

索引基本上按'键'排列你的表格。在您的情况下'thing1ID','ResultDate'。对表进行排序时,访问行比循环遍历整个表(2.7mil)要快得多,因为你没有行可能的线索。

即。 2,7,3,8,1,您需要搜索整个表格以获得数字1.但如果您有1,2,3,7,8。您只需检查第一个数字。

BUT!对于具有许多涉及“密钥”的更新/插入的表将减慢,因为您需要在每个条目之后对表进行排序。因此,找出最适合您的数据库的内容。

答案 1 :(得分:0)

对于您的查询,PK不是最佳选择,因为您正在对ResultDate进行范围搜索。 根据您的查询,您将搜索Thing1Id 23缩小到约。 8100万行仍然很多。

在您的查询中,对Thing1Id的搜索在23上修复,因此Thing1Id和ResultDate上的额外索引将是您查询的最佳选择。

答案 2 :(得分:0)

query execution plan会告诉你这里发生了什么,这通常比推测更好,但在这种情况下,我认为有足够的信息可以进行有根据的猜测。

首先,索引的INCLUDE ([Num],[Thing2Id])部分只意味着这两列的值在索引和表本身中都是重复的。它很有用,因为它可以防止SQL Server在该索引中执行查找后查看表本身以获取这些详细信息(在这种情况下索引是covering index)但是通常这种查找非常快,所以不太可能直接负责“大规模”改进的性能。我猜测以下指数的速度是99.9%。

CREATE NONCLUSTERED INDEX [IX_Missing]
ON [dbo].[Result]
(
    [Thing1Id],
    [ResultDate]
)

在我们继续其重要的事情之前,要了解SQL Server有两种方法来执行此查询(为了解释的目的而大大简化):

  1. 查找两个提供日期之间ResultDate的所有行,然后查看那些行Thing1Id为23
  2. 的行
  3. 查找Thing1Id为23的所有行,然后查看这些行,查找在提供的两个日期之间ResultDate的行
  4. 根据表中的数据,这些方法中的一种可能显着比另一种更快,例如,如果表中的大多数行的Thing1Id为23并且很少有匹配的ResultDate然后它可能会更快地使用第一种方法,因为它可以更快地消除更多的行。

    我们需要理解的另一个重要难题是,由于索引的工作方式,SQL无法在第二种情况下使用聚簇索引,因为Thing1Id列在之后 / em> ResultDate列(有点像要求某人使用书中的索引来查找第二个字母为“Q”的所有条目,然后然后询问他们只检查那些以“S”开头的单词


    因此我猜测为什么这个索引提高了性能只是因为SQL Server使用方法2(首先按Thing1Id过滤)而不是方法1更有效率。

    您应该能够使用查询执行计划来确认这一点。