1

SQL 小提琴:http ://sqlfiddle.com/#!3/23cf8

在此查询中,当我In在 Id 上有一个子句,然后还选择其他列时,首先评估 In,然后通过 RID 查找拉入 Details 列和其他列:

--In production and in SQL Fiddle, Details is grabbed via a RID Lookup after the In clause is evaluated
    SELECT [Id]
      ,[ForeignId]    
     ,Details    
      --Generate a numbering(starting at 1) 
      --,Row_Number() Over(Partition By ForeignId Order By Id Desc) as ContactNumber --Desc because older posts should be numbered last
  FROM SupportContacts
  Where foreignId In (1,2,3,5)

使用此查询,将通过表扫描获取详细信息。

With NumberedContacts AS 
(
    SELECT [Id]
      ,[ForeignId]
      --Generate a numbering(starting at 1) 
      ,Row_Number() Over(Partition By ForeignId Order By Id Desc) as ContactNumber --Desc because older posts should be numbered last
  FROM SupportContacts
  Where ForeignId In (1,2,3,5) 
)
Select nc.[Id]
      ,nc.[ForeignId]   
      ,sc.[Details]
From NumberedContacts nc
Inner Join SupportContacts sc on nc.Id = sc.Id
Where nc.ContactNumber <= 2 --Only grab the last 2 contacts per ForeignId
;

在 SqlFiddle 中,第二个查询实际上获得了 RID 查找,而在具有一百万条记录的生产环境中,它产生了表扫描(该IN子句消除了 99% 的行)

否则,SQL Fiddle 中显示的查询计划是相同的,唯一的区别是对于第二个查询,SQL Fiddle 中的 RID 查找是生产中的表扫描:(

  1. 我想了解导致这种行为的可能性?你会看什么样的东西来帮助确定它在这里使用表扫描的原因?

  2. 我如何影响它在那里使用 RID 查找?

从实际执行计划中的操作成本来看,如果我可以使用 RID 查找,我相信我可以让第二个查询在性能上非常接近第一个查询。如果我不选择该Detail列,那么这两个查询的性能在生产中非常接近。只有在添加其他列之后Detail,第二个查询的性能才会显着下降。当我把它放在 SQL Fiddle 中,看到执行计划使用了 RID Lookup 时,我很惊讶,但有点困惑......

它没有聚集索引,因为在使用不同聚集索引进行测试时,此查询和其他查询的性能稍差。那是在我开始添加其他列之前Details,我可以尝试更多,但是想在我开始在黑暗中使用随机索引拍摄之前了解现在发生了什么。

4

3 回答 3

2

如果您要更改主索引以包含该Details列怎么办?

如果您使用:

CREATE NONCLUSTERED INDEX [IX_SupportContacts_ForeignIdAsc_IdDesc] 
ON SupportContacts ([ForeignId] ASC, [Id] DESC)
INCLUDE (Details);

那么既不需要 RID 查找也不需要表扫描,因为您的查询可以仅从索引本身得到满足......

于 2012-12-08T08:34:51.610 回答
1

如果确实在使用相关索引 + RID 执行查询时会省略 99% 的行,那么您的生产环境中最可能的问题是您的统计信息已过时并且优化器没有意识到 ( 1,2,3,5) 会将结果集限制为总数据的 1%。

这是从 Pinal Dave 发现更多关于统计信息的好链接:http: //blog.sqlauthority.com/2010/01/25/sql-server-find-statistics-update-date-update-statistics/

至于在不更新统计信息的情况下强制优化器遵循正确的路径,您可以使用表提示 - 如果您知道您的计划应该使用的包含 ID 和 ForeignID 列的索引,然后将其粘贴在您的查询中作为提示和强制 SQL 优化器使用索引:

http://msdn.microsoft.com/en-us/library/ms187373.aspx

仅供参考,如果您想从第二个查询中获得最佳性能,请使用此索引并完全避免您遇到的头痛:

create index ix1 on SupportContacts(ForeignID, Id DESC) include (Details);
于 2012-12-08T20:56:08.447 回答
1

查询计划的差异将取决于存在的索引类型以及不同环境中这些表的数据统计信息。

优化器使用统计数据(主要是数据频率的直方图)和可用索引来决定哪个执行计划将是最快的。

因此,例如,您注意到包含“详细信息”列时性能会下降。这是一个几乎可以肯定的迹象,表明“详细信息”列不是索引的一部分,或者如果它是索引的一部分,则该列中的数据大多是唯一的,因此索引访问将是等效的(或几乎等效的)到表扫描。

通常当这种情况出现时,优化器会选择表扫描而不是索引访问,因为它可以利用块读取之类的东西来比索引的碎片读取更快地访问表记录。

为了影响优化器将选择的路径,您需要查看可以添加/修改以使索引访问更有效的可能索引,但这应该小心完成,因为它也会对其他查询产生不利影响可能会降低插入性能。

您可以帮助优化器执行的另一项重要活动是确保表统计信息保持最新并以适合表数据中频率分布的变化率的频率刷新

于 2012-12-08T00:03:52.113 回答