我正在处理一张非常大的桌子(每天大约增加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个联合表,数据每天从一个表转移到另一个表,并且每月对数据进行分区。)
答案 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有两种方法来执行此查询(为了解释的目的而大大简化):
ResultDate
的所有行,然后查看那些行Thing1Id
为23 Thing1Id
为23的所有行,然后查看这些行,查找在提供的两个日期之间ResultDate
的行根据表中的数据,这些方法中的一种可能显着比另一种更快,例如,如果表中的大多数行的Thing1Id
为23并且很少有匹配的ResultDate
然后它可能会更快地使用第一种方法,因为它可以更快地消除更多的行。
我们需要理解的另一个重要难题是,由于索引的工作方式,SQL无法在第二种情况下使用聚簇索引,因为Thing1Id
列在之后 / em> ResultDate
列(有点像要求某人使用书中的索引来查找第二个字母为“Q”的所有条目,然后然后询问他们只检查那些以“S”开头的单词
因此我猜测为什么这个索引提高了性能只是因为SQL Server使用方法2(首先按Thing1Id
过滤)而不是方法1更有效率。
您应该能够使用查询执行计划来确认这一点。