为什么SQL Server查询优化器有时会忽略明显的群集主键?

时间:2016-06-05 01:54:06

标签: sql-server sql-execution-plan

我一直在摸着这个。

我在select count(id)作为集群整数主键的表上运行简单id,SQL Optimizer完全忽略了它的查询执行计划中的主键,支持日期字段中的索引.... ???

实际表格:

CREATE TABLE [dbo].[msgr](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [dt] [datetime2](3) NOT NULL CONSTRAINT [DF_msgr_dt]  DEFAULT (sysdatetime()),
    [uid] [int] NOT NULL,
    [msg] [varchar](7000) NOT NULL CONSTRAINT [DF_msgr_msg]  DEFAULT (''),
    [type] [tinyint] NOT NULL,
    [cid] [int] NOT NULL CONSTRAINT [DF_msgr_cid]  DEFAULT ((0)),
    [via] [tinyint] NOT NULL,
    [msg_id] [bigint] NOT NULL,
 CONSTRAINT [PK_msgr] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

请问这可能是什么原因?

4 个答案:

答案 0 :(得分:3)

1)在我看来,这里的关键点是集群表(具有聚簇索引的表=主数据结构=是存储表数据的数据结构=聚集索引是表本身)每个非聚集索引也包括聚簇索引的键。这意味着

CREATE [UNIQUE] NONCLUSTERED INDEX bla 
ON [dbo].[msgr] (uid) 

基本相同
CREATE [UNIQUE] NONCLUSTERED INDEX bla 
ON [dbo].[msgr] (uid) 
INCLUDE (id) -- id = key of clustered index

因此,对于此类表,叶页上非聚集索引的每条记录也包含聚簇索引的键。这样,在每个非聚集索引和每个叶子记录中,SQL Server存储也会有一些指向主数据结构的指针。

2)这意味着SELECT COUNT(id) FROM dbo.msgr可以使用CI执行,但也可以使用NCI,因为两个索引都包含id(聚簇索引的键)列。

作为本主题中的第二个注释,因为IDENTITY属性(对于id列)表示必填列(NOT NULL),COUNT(id)与{ {1}}。此外,这意味着COUNT(*)(也是必填/ COUNT(msg_id))列与NOT NULL相同。因此,COUNT(*)的执行计划很可能会使用相同的NCI(例如SELECT COUNT(msg_id) FROM dbo.msgr)。

3)非聚集索引的大小小于聚簇索引。这意味着更少的IO =>从性能的角度来看,使用NCI比使用CI更好。

我会做一些简单的测试:

bla

如果SET STATISTICS IO ON; GO SELECT COUNT(id) FROM dbo.[dbo].[msgr] WITH(INDEX=[bla]) -- It forces usage of NCI GO SELECT COUNT(id) FROM dbo.[dbo].[msgr] WITH(INDEX=[PK_msgr]) -- It forces usage of CI GO SET STATISTICS IO OFF; GO 表中有大量数据,那么msgr将显示不同的STATISTICS IO(逻辑IO),更少的NCI查询LIO

答案 1 :(得分:2)

SQL Server有一个非常好的优化器。如果它选择非聚集索引,那么这可能是最好的。这是我的解释。

索引在辅助树状数据结构中存储键和中断值的列表。对于非聚簇索引,索引的叶子是记录标识符(指向记录的指针)。

聚集索引没有叶子。数据页本身就是叶子。因此,使用聚簇索引计算记录数需要读取所有数据页。说实话,我可能认为有一种方法可以避免读取数据页读取,但可能需要读取数据页。

在任何情况下,非聚集索引都不需要读取原始数据页,因为所有用于计数的信息都在索引中。

答案 2 :(得分:1)

SQL Server只是遍历索引的叶子以进行计数。它不必转到数据。 SQL Server已选择该索引来计算所有指针以获取计数。

答案 3 :(得分:1)

  

我在id为集群整数主键的表上运行简单的select count(id),SQL Optimizer完全忽略了它的查询执行计划中的主键,支持日期字段的索引.... ???

SQL Server是一种基于成本的optmizer,每当它选择一个计划时,都需要考虑两件事

  

1.查询的总费用
  2.在合理的时间内选择计划。

SQL服务器查询优化器不知道缓存中有多少页面。所以它总是假定它必须从磁盘读取它们。

现在考虑上面的事情..

  

SQL可能会考虑扫描最窄的索引,因为id是主键,而且不会为null(count(id)排除空值)加上扫描这需要扫描所有大的索引,所以请选择扫描另一个索引,可能是一个非空的索引。