日期范围查询的 SQL 索引

标签 sql database indexing sql-server-2014 database-performance

几天来,我一直在努力提高我的数据库的性能,对于 SQL Server 数据库中的索引,我仍然对一些问题感到困惑。

我会尽量提供信息。

我的数据库目前包含大约 10 万行,并且会继续增长,因此我正在努力寻找一种方法使其运行得更快。

我也在写这个表,所以如果你的建议会大大减少写作时间,请告诉我。

总体目标是选择日期范围内具有特定名称的所有行。

这通常是从很多行中选择超过 3,000 行,哈哈 ...

表架构:

CREATE TABLE [dbo].[reports]
(
    [id] [int] IDENTITY(1,1) NOT NULL,
    [IsDuplicate] [bit] NOT NULL,
    [IsNotValid] [bit] NOT NULL,
    [Time] [datetime] NOT NULL,
    [ShortDate] [date] NOT NULL,
    [Source] [nvarchar](350) NULL,
    [Email] [nvarchar](350) NULL,

    CONSTRAINT [PK_dbo.reports] 
        PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]

这是我正在使用的 SQL 查询:

SELECT * 
FROM [db].[dbo].[reports]
WHERE Source = 'name1' 
  AND ShortDate BETWEEN '2017-10-13' AND '2017-10-15'

据我所知,在不影响编写时间的情况下提高效率的最佳方法是在 SourceShortDate 上创建非聚集索引。

我喜欢这样的索引模式:

CREATE NONCLUSTERED INDEX [Source&Time] 
ON [dbo].[reports]([Source] ASC, [ShortDate] ASC)

现在我们进入了让我完全迷失的棘手部分,上面的索引有时有效,有时半有效,有时根本无效....

(不确定这是否重要,但目前 90% 的数据库行具有相同的源,尽管这种情况不会持续很长时间)

  1. 对于下面的查询,根本没有使用索引,我使用的是 SQL Server 2014,在执行计划中它说它只使用聚集索引扫描:

    SELECT * 
    FROM [db].[dbo].[reports]
    WHERE Source = 'name1' 
      AND ShortDate BETWEEN '2017-10-10' AND '2017-10-15'
    
  2. 在这个查询中,索引根本没有被使用,尽管我从 SQL Server 得到一个建议,创建一个日期在前,源在后的索引...我读到索引应该是查询的顺序是?它还说要包括我选择的所有列,这是必须的吗?...我再次读到我应该只在索引中包括我正在搜​​索的列。

    SELECT * 
    FROM [db].[dbo].[reports]
    WHERE Source = 'name1' 
      AND ShortDate = '2017-10-13'
    

    SQL Server 索引建议 -

    /* The Query Processor estimates that implementing the following 
       index could improve the query cost by 86.2728%. */
    
    /*
    USE [db]
    GO
    
    CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
    ON [dbo].[reports] ([ShortDate], [Source])
    INCLUDE ([id], [IsDuplicate], [IsNotValid], [Time], [Email])
    GO
    */
    

现在我尝试使用 SQL Server 建议我创建的索引并且它有效,看起来它使用上述两个查询使用 100% 的非聚集索引。

我尝试使用此索引,但删除了包含的列但它不起作用...似乎我必须在索引中包含我正在选择的所有列?

顺便说一句,如果我包含所有列,它也可以在使用我创建的索引时使用。

总而言之:索引的顺序似乎并不重要,因为它在创建 Source + ShortDateShortDate + Source 时都有效

但出于某种原因,必须包含所有列...(这将极大地影响写入此表?)

非常感谢阅读,我的目标是了解为什么会发生这种情况以及我应该做些什么(不仅仅是解决方案,因为我还需要将其应用到其他项目中)。

干杯:)

最佳答案

SQL Server 中的索引部分是长期经验(以及许多小时的挫折)的专有技术,部分是黑魔法。不要为此自责太多 - 这就是像 SO 这样的地方的理想之处 - 大量的大脑,大量的优化经验,您可以利用它们。

I read that the index should be made by the order the query is?

如果您读到这篇文章 - 它绝对不正确 - 列的顺序相关的 - 但以不同的方式:复合索引(由多个列)只有在您在查询的索引定义中指定n 最左边的列时才会被考虑。

经典示例:电话簿的索引为(城市、姓氏、名字)。这样的索引可能会被使用:

  • 在其 WHERE 子句中指定所有三列的查询中
  • 在使用 citylastname 的查询中(查找“Detroit”中的所有“Miller”)
  • 或在仅按城市过滤的查询中

但如果您只想搜索firstname,则可以NEVER EVER使用它......这就是复合索引的技巧你需要知道。但是,如果您总是使用索引中的所有列,那么它们的顺序通常并不真正相关 - 查询优化器会为您处理这个问题。


至于包含的列 - 它们存储在非聚集索引的叶级中 - 它们不是搜索的一部分索引的结构,并且您不能为 WHERE 子句中包含的那些列指定过滤器值。

这些包含的列的主要好处是:如果您在非聚集索引中搜索,最后您实际上找到了您正在寻找的值 - 此时您有什么可用的?非聚集索引将存储非聚集索引定义中的列(ShortDateSource),并将存储聚集键(如果你有一个 - 你应该!) - 但没有别的。

因此在这种情况下,一旦找到匹配项,并且您的查询需要该表中的所有内容,SQL Server 必须执行所谓的键查找(通常也称为书签查找),它获取聚集键,然后对聚集索引执行Seek操作,以到达包含所有内容的实际数据页您正在寻找的值。

如果您的索引中有包含的列,那么您的非聚集索引的叶级页面包含

  • 非聚集索引中定义的列
  • 集群键列
  • 所有那些额外的列在您的INCLUDE语句中定义

如果这些列“涵盖”了您的查询,例如提供查询所需的所有值,然后 SQL Server 在找到您在非聚集索引中搜索的值后完成 - 它可以从非聚集索引的叶级页面中获取所需的所有值,并且它 不需要对聚类索引进行另一次(昂贵的)键查找以获取实际值。

正因为如此,在您的 SELECT 中尝试始终明确指定真正需要的那些列可能是有益的 - 在这种情况下,您也许能够创建一个高效的覆盖索引,为您的SELECT 提供所有值 - 始终使用SELECT * 使这变得非常困难或几乎不可能......

关于日期范围查询的 SQL 索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46766798/

相关文章:

Javascript在div中查找所选文本的出现位置

MySQL 带有连接的 SELECT 查询花费太长的时间

c# - 我需要一种用于定位 SQL Server 实例的机制

performance - 如何在 Heroku 上重置 pg_stat_user_tables? (pg_stat_reset() 需要 super 用户)

php - 如何发布到具有一对多关系的多个数据库表

c# - 定期运行 C# 应用程序以更新数据库

postgresql - 如何在postgres中使用jsonb索引

sql - 使用带连接的占位符

sql - N前缀和参数

mysql - 在过程中加入 2 个字符串作为查询