SQL Server为何在索引过滤器时读取所有行?

时间:2018-04-17 15:40:08

标签: sql-server optimization indexing

上下文

我正在优化我的表并专门添加索引以减少读取/查询。标识为具有高读取的表之一是Users表。但是,在ID上过滤的列已经是索引。为什么通过索引执行如此多的读取来过滤简单查询?

该表有大约200行。大约一半的时间是针对它运行查询(根据SQL Server Profiler),它执行112次读取需要16ms。我意识到这些数字本身都不高,但由于这是一个非常常见的查询,应该在> 1ms内执行1次读取,我很想知道如何进一步调试。

CREATE TABLE [dbo].[Users]
(
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](15) NULL,
    [Password] [varchar](128) NULL,
    [Code] [varchar](20) NULL,
    [ResetCode] [varchar](32) NULL,
    [ResetTime] [datetime] NULL,
    [LastLogin] [datetime] NULL,
    [LastIP] [varchar](50) NULL,
    [UserSettings] [varchar](max) NULL,
    [LastName] [varchar](30) NULL,
    [FirstName] [varchar](30) NULL,
    [Middle] [varchar](30) NULL,
    [Address1] [varchar](50) NULL,
    [Address2] [varchar](50) NULL,
    [City] [varchar](50) NULL,
    [State] [varchar](4) NULL,
    [Zip] [varchar](10) NULL,
    [Email] [varchar](80) NULL,
    [Flag] [tinyint] NULL,
    [Inactive] [tinyint] NULL,

    CONSTRAINT [PK_Users] 
        PRIMARY KEY CLUSTERED ([ID] ASC)
)

查询

SELECT
    ID AS id,
    Name AS username,
    FirstName AS first_name,
    LastName AS last_name,
    Email AS email,
    Flag AS flag
FROM Users
WHERE ID = 180

执行计划

这是我手动执行它所以不确定它是否与从php web app发送时使用的相同。 https://www.brentozar.com/pastetheplan/?id=HkCx99Xnf

1 个答案:

答案 0 :(得分:0)

好吧,它需要读取所有列,因为你没有包含select语句中所有列的覆盖索引。

您需要创建一个使用ID列的索引,并包含您需要的列。

请注意,并非所有版本的SQL Server都有覆盖索引, 语法是:

create index [IX_SingeUsers] on  [dbo].[Users] (ID)
include ([Name],[FirstName],[LastName],[Email],[Flag])

对于是,请随意为索引命名,而不是在我的示例中 IX_SingeUsers

或者,您可以创建具有覆盖索引的视图,并针对该视图进行选择。带索引的视图是"表"并作为SQL Server内部的表存储和维护。然后,您只需拥有您正在寻找的数据并且速度非常快,并且在覆盖索引的更多许可模型中最好。

覆盖索引绑定到表架构,如果您想更新表,首先需要删除视图,然后在完成表更改后重新创建它。

索引视图记录为here,更改版本以获取您部署的版本的正确信息。

更新: 请注意,这不会读取所有行,它只会读取落在索引统计信息中的那些扩展区(希望您更新它们)的聚集索引和所提到的覆盖索引,这可能只有1个扩展并且只有那些索引中的列也可能适合这8个数据页。它不需要转到表,因为索引提取将提供查询需要返回的所有数据。

请记住,SQL会在数据库中锁定此数据,以确保没有数据损坏。如果您有许多用户且没有分区,则可能会发生表锁定。与磁盘读取相比,这对性能的影响要大得多。查看磁盘队列长度以查看是否存在磁盘延迟问题。

快乐编码

沃尔特

相关问题