Why does SQL Server query optimizer sometimes overlook obvious clustered primary key?

两盒软妹~` 提交于 2019-12-10 11:23:49

问题


I have been scratching my head on this one.

I run as simple select count(id) on a table with id as clustered integer primary key and the SQL Optimizer totally ignores the primary key in it's query execution plan, in favor of an index on a date field.... ???

Actual table:

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]

Please what could be the reason for this?


回答1:


1) In my opinion, the key point here is that for clustered tables (tables which have a clustered index = the main data structure = is that data structure that store table data = clustered index is the table itself) the every non-clustered index include also the the key of clustered index. This means that

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

is basically the same thing as

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

So, for such tables, every record from non-clustered indexes on the leaf pages includes also the key of clustered index. This way, within every non-clustered index and for every leaf record SQL Server store also some kind of pointer to the main data structure.

2) This means that SELECT COUNT(id) FROM dbo.msgr can be executed using CI but also using NCI because both indexes include the id (key of clustered index) column.

As a secondary note within this topic, because IDENTITY property (for id column) means a mandatory column (NOT NULL), COUNT(id) is the same thing as COUNT(*). Also, this means that COUNT(msg_id) (also a mandatory / NOT NULL) column is the same thing as COUNT(*). So, it's very likely that execution plan for SELECT COUNT(msg_id) FROM dbo.msgr will use the same NCI (for example bla).

3) Non-clustered indexes have smaller size than clustered index. This means also less IO => It's better from performance point of view to use the NCI than CI.

I would do following simple test:

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

If there is a lot of data within msgr table then STATISTICS IO will show different LIO (logical IO), with less LIO for NCI query.




回答2:


SQL Server has a pretty good optimizer. If it is choosing a non-clustered index, then that is probably best for this purpose. This is my explanation.

An index stores lists of keys and break values in an auxiliary tree-like data structure. For non-clustered indexes, the leaves of the index are record identifiers (pointers to records).

A clustered index doesn't have leaves. The data pages themselves are the leaves. Hence, counting the number of records using a clustered index requires reading all the data pages. To be honest, I might think there was a way to avoid the reading the data page reads, but reading the data pages might be necessary.

In any case, a non-clustered index doesn't require reading the original data pages, because all the information for counting is in the index.




回答3:


SQL Server just goes through the leaves of the index for a count. It does not have to go to the data. SQL Server has chosen that index to count all the pointers to get a count.




回答4:


I run as simple select count(id) on a table with id as clustered integer primary key and the SQL Optimizer totally ignores the primary key in it's query execution plan, in favor of an index on a date field.... ???

SQL server is a cost based optmizer,Whenever it chooses a plan,it takes two things into account

1.Total cost of the query
2.Choosing the plan in a reasonable amount of time.

One thing SQL server query optimizer is not aware of is how many pages are there in cache.So it always assumes it has to do read them from disk..

Now considering above things into account..

SQL might have thought to scan the narrowest possible index since id is a primary key and this wont be null (count (id) excludes null values)plus scanning this requires to scan all of the index which is big,so instead choose to scan another index narrow one possibly which is not null.



来源:https://stackoverflow.com/questions/37636716/why-does-sql-server-query-optimizer-sometimes-overlook-obvious-clustered-primary

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!