【问题标题】:SQL Server why are all rows read when filter is indexed?SQL Server 为什么在过滤器被索引时读取所有行?
【发布时间】:2018-09-27 16:00:05
【问题描述】:

上下文

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

该表大约有 200 行。大约有一半的时间针对它运行查询(根据 SQL Server Profiler),它执行 112 次读取,需要 16 毫秒。我意识到这些数字本身并不高,但由于这是一个非常常见的查询,并且应该在 >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 应用程序发送时使用的相同。 https://www.brentozar.com/pastetheplan/?id=HkCx99Xnf

【问题讨论】:

  • 分享你的执行计划。 brentozar.com/pastetheplan
  • 我不确定执行计划是什么,但这是 sql server 2008,所以无论它自动为我生成什么。
  • 点击我分享的链接,然后点击说明。
  • 我没有注意到你的索引是聚集的,所以我删除了我的评论。但是,我们仍然需要计划。其次,这个索引有多碎片化?如果您删除并重新创建索引(因为行数太少)或只是重建索引,您可能会看到这种变化。
  • @scsimon - 因为使用的键是主键,所以数据和键一起保存,所以一旦找到 kay,数据就在同一个地方,不需要进一步读取那一排。确实需要执行计划……

标签: sql-server optimization indexing


【解决方案1】:

好吧,它需要读取所有列,因为您没有包含 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 将此数据锁定在数据库中,以确保没有数据损坏。如果您有很多用户并且没有分区,则可能会发生表锁。与从磁盘读取相比,这对性能的影响要大得多。查看磁盘队列长度,看看是否存在磁盘延迟问题。

【讨论】:

  • 我很困惑如果要读取所有行,身份索引的意义何在?如果一个表有 200 万行并且我想要行 id 为 100 万的 5 列,它会执行 100 万次读取吗?我认为这是不正确的,除非我实际上是按这些字段进行过滤。
  • 嗨丹尼尔森,在上面添加了答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 2014-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多