将 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 的含义....
- 在您创建
PRIMARY KEY CLUSTERED
的原始 CREATE TABLE
中,它指定要聚类的列,按照它们聚类(存储)在 [=43= 中的顺序].
[FeatureID]
[ParameterID]
[MeasurementDateTime]
如果您 运行 一个包含特定 FeatureID
的 WHERE
子句的查询,那么它将能够 seek
到索引的那一部分。
但是您没有在查询中这样做。
您已使用 WHERE FeatureID LIKE 'AS%' ...
查询引擎无法查找,因为带有尾随通配符 %
的 LIKE
意味着它必须扫描所有以字母 AS
开头的 FeatureID,然后在每个树中的那些节点查看是否有匹配 ParameterID = 'AP2' AND YEAR(MeasurementDateTime) = 2015
.
的记录
- 在您的非聚集索引中,您使用了不同的列顺序:
[ParameterID]
[FeatureID]
当您 运行 相同的查询时,它可以 seek
因为您在 WHERE
子句中指定了一个确切的 ParameterID
。
顺序很重要! SQL 索引是 sortof B-Tree 数据结构,如果不创建多个索引,则无法以不同的顺序物理存储(或遍历)它们。创建过多的索引可能会给数据库带来过多的开销,因此可以创建有助于大多数查询的索引,但不要创建太多。这主要涉及了解哪种查询经常 运行 针对您的数据库并进行相应的调整。
我有 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 的含义....
- 在您创建
PRIMARY KEY CLUSTERED
的原始CREATE TABLE
中,它指定要聚类的列,按照它们聚类(存储)在 [=43= 中的顺序].
[FeatureID] [ParameterID] [MeasurementDateTime]
如果您 运行 一个包含特定 FeatureID
的 WHERE
子句的查询,那么它将能够 seek
到索引的那一部分。
但是您没有在查询中这样做。
您已使用 WHERE FeatureID LIKE 'AS%' ...
查询引擎无法查找,因为带有尾随通配符 %
的 LIKE
意味着它必须扫描所有以字母 AS
开头的 FeatureID,然后在每个树中的那些节点查看是否有匹配 ParameterID = 'AP2' AND YEAR(MeasurementDateTime) = 2015
.
- 在您的非聚集索引中,您使用了不同的列顺序:
[ParameterID] [FeatureID]
当您 运行 相同的查询时,它可以 seek
因为您在 WHERE
子句中指定了一个确切的 ParameterID
。
顺序很重要! SQL 索引是 sortof B-Tree 数据结构,如果不创建多个索引,则无法以不同的顺序物理存储(或遍历)它们。创建过多的索引可能会给数据库带来过多的开销,因此可以创建有助于大多数查询的索引,但不要创建太多。这主要涉及了解哪种查询经常 运行 针对您的数据库并进行相应的调整。