【问题标题】:Index seek and Table scan takes the same time to execute索引查找和表扫描执行时间相同
【发布时间】:2018-10-24 15:07:47
【问题描述】:

我有一个结构如下的表:

CREATE TABLE [dbo].[User]
(
    [Id] [INT] IDENTITY(1,1) NOT NULL,
    [CountryCode] [NVARCHAR](2) NOT NULL DEFAULT (N'GB'),
    [CreationDate] [DATETIME2](7) NOT NULL,
    [Email] [NVARCHAR](256) NULL,
    [EmailConfirmed] [BIT] NOT NULL,
    [FirstName] [NVARCHAR](MAX) NOT NULL,
    [LastName] [NVARCHAR](MAX) NOT NULL,
    [LastSignIn] [DATETIME2](7) NOT NULL,
    [LockoutEnabled] [BIT] NOT NULL,
    [LockoutEnd] [DATETIMEOFFSET](7) NULL,
    [NormalizedEmail] [NVARCHAR](256) NULL,
    [NormalizedUserName] [NVARCHAR](256) NULL,
    [PasswordHash] [NVARCHAR](MAX) NULL,
    [SecurityStamp] [NVARCHAR](MAX) NULL,
    [TimeZone] [NVARCHAR](64) NOT NULL DEFAULT (N'Europe/London'),
    [TwoFactorEnabled] [BIT] NOT NULL,
    [UserName] [NVARCHAR](256) NULL,
    [LastInfoUpdate] [DATETIME] NOT NULL
)

我在该表中有大约一百万行,我想将非聚集索引应用于[LastInfoUpdate] 列。

所以我使用这个命令创建了一个非聚集索引:

CREATE NONCLUSTERED INDEX IX_ProductVendor_VendorID1   
ON [dbo].[TestUsers] (LastInfoUpdate)
INCLUDE(Email)

一旦我尝试运行这样的简单查询:

  SELECT [LastInfoUpdate]
  FROM [dbo].[TestUsers]
  WHERE [LastInfoUpdate] >= GETUTCDATE()

我只是在时间上得到与没有索引相同的结果。根据带有 db 的 SQL Server Profiler 在使用索引时会进行索引搜索,与没有索引的情况相比,它只使用更少的 cpu 资源,但对我来说重要的是时间到了。几点一样?我做错了什么?

Execution Plan of table scan

Execution plan of Index Scan

Index seek Execution Plan file

【问题讨论】:

  • 这条语句返回多少行?
  • @RBarryYoung 大约百万美元
  • 那么这就是为什么它需要相同的时间。几乎所有的时间都只是在50万行的数据传输时间。
  • 猜你是对的。那么我怎样才能消除这个的执行时间呢?
  • 请使用以下页面粘贴查询计划:brentozar.com/pastetheplan

标签: sql sql-server tsql indexing


【解决方案1】:

只需创建以下索引:

CREATE INDEX IX_Users_EventDate ON Users(EventDate)
INCLUDE (EventId)

而且下面的查询会很快:

SELECT EventId, EventDate
FROM Users
WHERE EventDate <= GETUTCDATE()

因为索引是覆盖索引。

覆盖索引的键必须包含在WHEREORDER BY 子句中引用的列。并且覆盖索引必须包含SELECT 列表中引用的所有列。

您发布的查询与您链接的查询计划不匹配。查询计划适用于上述查询。

要考虑的另一件事是查询返回的记录数。如果它们很多,查询就不够快,因为它需要读取所有数据并将其发送到网络。

【讨论】:

    【解决方案2】:

    尝试使用ColumnStore 索引。当您想要获得一定范围的列时,它会更快:

    CREATE NONCLUSTERED COLUMNSTORE INDEX 
        [csi_User_LastInfoUpdate_Email] ON 
        [dbo].[User] ( [LastInfoUpdate], [Email] )WITH 
        (DROP_EXISTING = OFF, COMPRESSION_DELAY = 0) ON [PRIMARY]
    

    An article about column store index.

    【讨论】:

    • 感谢您的建议,但在我的版本中不支持列存储
    【解决方案3】:

    "WHERE [LastInfoUpdate] >= GETUTCDATE()" 可能会返回很多结果。在这种情况下,Tablescan 可以比索引查找和随后从 tabledata 添加信息更快。 通过将查询到的信息添加到索引中,您可以避免昂贵的后续查找表数据。

    【讨论】:

    • 但我需要例如找到所有大于现在加上 10 分钟的日期,并将其搜索到数百万行。在sql中有哪些可能的方法?
    • 将 (LastInfoUpdate,EventDateTime) 作为索引的列定义有帮助吗?您可以查看的数据更少,而索引可以提供所有数据。这肯定比 tablescan 快
    • 没有。还是尝试基于日期时间字段创建索引是个坏主意?
    • 为什么这是个坏主意?可以订购日期时间,您可以进行有序查询,因此至少如果您从日期时间字段作为属性列表开始,索引应该有助于处理查询
    猜你喜欢
    • 1970-01-01
    • 2012-01-31
    • 1970-01-01
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 2010-11-11
    • 2020-02-16
    相关资源
    最近更新 更多