将 non-clustered 索引添加到 table 以提高性能

Adding non-clustered index to a table to increase performance

我有 table 结构如下

CREATE TABLE [dbo].[AIRQUALITYTS2]
(
    [FeatureID] [nvarchar](20) NOT NULL,
    [ParameterID] [nvarchar](20) NOT NULL,
    [MeasurementDateTime] [datetime2](7) NOT NULL,
    [ParameterValue] [numeric](38, 8) NULL,
    [Remarks] [nvarchar](150) NULL,

    CONSTRAINT [PK_AIRQUALITYTS2] 
        PRIMARY KEY CLUSTERED ([FeatureID] ASC, [ParameterID] ASC, [MeasurementDateTime] ASC)
                    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                          ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

当我执行这个查询时:

set statistics io on

SELECT 
    COUNT(featureid), featureid 
FROM
    AIRQUALITYTS2 
WHERE
    FeatureID LIKE 'AS%' 
    AND ParameterID = 'AP2' 
    AND YEAR(MeasurementDateTime) = 2015
GROUP BY 
    FeatureID
ORDER BY 
    FeatureID

我看到逻辑记录101871,查询执行计划是

但是当我在 table 上添加 non-clustered 索引时

 CREATE NONCLUSTERED INDEX non_fidpidmdate
     ON [dbo].[AIRQUALITYTS2] ([ParameterID], [FeatureID])
     INCLUDE ([MeasurementDateTime])

当我执行相同的查询时,我看到逻辑记录只读取 4636,而且速度非常快,查询执行计划是

问题一:当第二次查询逻辑记录较少时

问题 2:为什么第一个查询使用 聚簇索引扫描,尽管它在添加 non-cluster 后在 featureid、ParameterID 和 MeasurementDateTime 上有聚簇索引,但如第一张图片所示index 它使用 Index Seek (Non-Clustered) images

中显示的第二张图片

注意:我已将 where 子句更改为

MeasurementDateTime >= '2004-01-01 00:00:00' 
and MeasurementDateTime <= '2004-12-31 00:00:00' 

使其可搜索,但结果仍然相同。

对于问题1:由于你的索引是覆盖(它包含了查询要检索的所有数据,以及查询和排序所需要的),查询可以是运行 完全针对索引(及其数据页)并使用 seek,这显然比扫描整个 table(聚簇索引扫描 = table扫描)及其所有数据。

不确定问题 #2 的含义....

  1. 在您创建 PRIMARY KEY CLUSTERED 的原始 CREATE TABLE 中,它指定要聚类的列,按照它们聚类(存储)在 [=43= 中的顺序].
[FeatureID]
[ParameterID]
[MeasurementDateTime]

如果您 运行 一个包含特定 FeatureIDWHERE 子句的查询,那么它将能够 seek 到索引的那一部分。

但是您没有在查询中这样做。 您已使用 WHERE FeatureID LIKE 'AS%' ...

查询引擎无法查找,因为带有尾随通配符 %LIKE 意味着它必须扫描所有以字母 AS 开头的 FeatureID,然后在每个树中的那些节点查看是否有匹配 ParameterID = 'AP2' AND YEAR(MeasurementDateTime) = 2015.

的记录
  1. 在您的非聚集索引中,您使用了不同的列顺序:
[ParameterID]
[FeatureID]

当您 运行 相同的查询时,它可以 seek 因为您在 WHERE 子句中指定了一个确切的 ParameterID

顺序很重要! SQL 索引是 sortof B-Tree 数据结构,如果不创建多个索引,则无法以不同的顺序物理存储(或遍历)它们。创建过多的索引可能会给数据库带来过多的开销,因此可以创建有助于大多数查询的索引,但不要创建太多。这主要涉及了解哪种查询经常 运行 针对您的数据库并进行相应的调整。