【问题标题】:SQL Server 2016 non clustered index is not being used while ordering on clustering index对聚集索引进行排序时未使用 SQL Server 2016 非聚集索引
【发布时间】:2019-09-12 15:17:47
【问题描述】:

您能否向我解释一下为什么在查询和对聚集索引进行排序时不使用此非聚集索引?

CREATE TABLE [dbo].[table]
(
    [NPId] [BIGINT] IDENTITY(1,1) NOT NULL, 
    [RequestDate] [DATETIME2](2) NOT NULL,
    [Status] [TINYINT] NOT NULL,
    [StatusCodeId] [SMALLINT] NULL,
    [NumberCount] [INT] NULL,
    [Number] [BIGINT] NULL,

    CONSTRAINT [PK_NPLog_1] 
       PRIMARY KEY CLUSTERED ([NPId] ASC)
               WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                     IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                     ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_ReqDate] 
ON [dbo].[table] ([RequestDate] ASC)
INCLUDE ([NPId])

查询:

DECLARE @date datetime2(2) = '2018.07.10'
DECLARE @date2 datetime2(2) = '2018.08.10'

SELECT TOP 10 npl.NPId 
FROM [table] npl  
WHERE npl.RequestDate >= @date 
  AND npl.RequestDate < @date2 
ORDER BY npid

如果我在 requestDate 上设置顺序,则使用索引。

【问题讨论】:

  • 所以 SQL Server 决定过滤器比order by 更重要。
  • 表格有多少行?如果有太多,那么 SQL Server 可能会发现处理由 npid 排序的行希望找到前 10 个匹配行是有效的。
  • 考虑到您通过NPid 订购并返回TOP 10,我猜估计器认为 CLUSTERED 索引是更好的选择。如果这是它得出的结论,它可能就是这样。
  • SQL Server 将估计 30% 的行匹配 RequestDate 谓词(因为实际值被变量隐藏,所以它必须猜测)并且它们在表中均匀分布,因此它只需要阅读 33 以获取由 NPId 订购的 TOP 10。实际上,由于日期范围只有一个月,因此匹配百分比可能要低得多,并且它们可能与 w.r.t. 相关。 NPId 所以它们都聚集在一起,这不是一个好计划......另一种方法是对匹配RequestDate 范围的所有行进行排序,然后得到TOP 10,这可能成本更高
  • 实际上 30% 是用于单个不等式谓词的猜测。对于&gt;=&lt;,它会略低(0.3) * SQRT(0.3) = 16.4% (dependant on CE version) - 所以估计61 行以获得TOP 10

标签: sql-server indexing b-tree


【解决方案1】:

这很容易重现。

只需将以下虚拟数据插入到您问题中的表中...

insert into [dbo].[table]
SELECT TOP 1000000 DATEADD(SECOND,  CRYPT_GEN_RANDOM(4)% 1000000,GETDATE()) , 1, 1, 1, 1
FROM sys.all_objects o1, sys.all_objects o2

然后运行

DECLARE @date datetime2(2) = '2018.07.10'
DECLARE @date2 datetime2(2) = '2018.08.10'

SELECT TOP 10 npl.NPId 
FROM [table] npl  
WHERE npl.RequestDate >= @date 
  AND npl.RequestDate < @date2 
ORDER BY npid
OPTION (QUERYTRACEON 9130) /*so filter visible in the plan*/


SELECT TOP 10 npl.NPId 
FROM [table] npl  WITH (INDEX = IX_ReqDate) 
WHERE npl.RequestDate >= @date 
  AND npl.RequestDate < @date2 
ORDER BY npid

以上使用的是CardinalityEstimationModelVersion = 140 - 在其他版本的 CE 中也会看到类似的情况,尽管确切的数字可能不同。

  1. 数据范围中使用的值隐藏在变量中,因此 SQL Server 假定它将匹配 16.431676725155% 行 (0.30*SQRT(0.30))。
  2. SQL Server 还假定这些行将均匀分布在整个表中。因此它假设它需要读取 60.8581 (10/(0.30*SQRT(0.30))) 才能获得 10 个匹配谓词。
  3. 对于另一个计划,它假定它需要对表的 16.4% 进行排序,而成本要高得多。

您可以尝试添加OPTION (RECOMPILE),以便 SQL Server 嗅探变量使用的日期范围,它可能将 16.4% 的估计值降低到足以更改计划的程度。

但更有可能的是,估计值减少但不足以更改计划,并且 CI 计划的成本仍然过低(因为与范围匹配的行在整个表中分布不均匀,必须读取比估计更多的行)。在这种情况下,您可以考虑使用索引提示来让它选择您想要的索引。

【讨论】:

    猜你喜欢
    • 2013-08-20
    • 2014-11-01
    • 2012-10-01
    • 2017-04-01
    • 2018-05-08
    • 2014-04-27
    • 2013-03-22
    • 1970-01-01
    相关资源
    最近更新 更多