我一直在摸着这个。
我在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]
请问这可能是什么原因?
答案 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)排除空值)加上扫描这需要扫描所有大的索引,所以请选择扫描另一个索引,可能是一个非空的索引。