【翻译】SQL Server索引进阶:第七级,过滤的索引

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介:

过滤一个索引

创建一个包含where子句的过滤的非聚集索引。

 

 
  1. IF EXISTS ( SELECT  * 
  2.             FROM    sys.indexes 
  3.             WHERE   OBJECT_ID = OBJECT_ID('Sales.SalesOrderDetail'
  4.                     AND name = 'FI_SpecialOfferID' )  
  5.     DROP INDEX Sales.SalesOrderDetail.FI_SpecialOfferID ; 
  6. GO 
  7.  
  8. CREATE INDEX FI_SpecialOfferID 
  9. ON Sales.SalesOrderDetail (SpecialOfferID) 
  10. WHERE SpecialOfferID; 

过滤一个索引的主要原因,是为了从索引中消除一个或者多个不经常被选择的值。看一下SalesOrderDetail表中的SpecialOfferID列,121317行的数据包含了12个不同的SpecialOfferID值,从1到16,每个值对应的行数如下。

SpecialOfferID RowCount
-------------- -----------

1              115884
2              3428
3              606
13             524
14             244
16             169
7              137
8              98
11             84
4              80
9              61
5              2

大部分的行,超过95%,SpecialOfferID的值是1.在SpecialOfferID列的非聚集索引对于SpecialOfferID=1的查询没有好处。查询将使用表扫描来查询115884行数据。但是,所以对于SpecialOfferID=5的查询是有好处的。

本文开头的创建索引对于115884行SpecialOfferID=1的数据行没有入口。因此索引很小,很高效,只包含5433个入口。

在我们的SalesOrderDetail例子中,需要过滤的主要值是“1”。在你自己的应用中,最常见的可能是NULL。在典型的事务数据库中,在可空的列中,如果null值占多数,NOT NULL就是例外。在这些列上创建索引的时候,要考虑过滤null值。

概念证明

为了证明过滤索引的好处,我们将六次执行下面的查询:

  • 三次在没有过滤的索引上执行,SpecialOfferID的值分别是:1,13,14。
  • 三次在有过滤的索引上执行,参数和上面的一样。

 

 
  1. SELECT  * 
  2. FROM    Sales.SalesOrderDetail 
  3. WHERE   SpecialOfferID = parameter value
  4. ORDER BY SpecialOfferID ; 

在上面的统计中可以看出来,1的行数占95%,13的行数占4%,14的行数占2%。

和往常一样,我们使用IO读取次数作为主要的衡量指标,同时打开SQL Server管理器的“显示执行计划”选项,观察每次查询的执行计划。

执行结果的统计如下。

WITH UNFILTERED INDEX:
Parameter Value Reads Plan
1 1238 Table scan
13 1238 Table scan
14 758 Retrieve bookmark values from index. Use them to retrieve rows from table
WITH FILTERED INDEX:
Parameter Value Reads Plan
1 1238 Table scan
13 1238 Table scan
14 758 Retrieve bookmark values from index. Use them to retrieve rows from table.

 

从上面可以看出来吗,不管用没用到索引,结果是一样的。换句话说,过滤的索引和非过滤的索引带来的好处是一样的。在不影响查询效率的情况下,我们节省了大量的磁盘空间。

过滤,查询,覆盖

在上面的例子中,对同一个索引来说,过滤列和索引键列是同一个列。当我们通过where子句指定过滤的时候,我们告诉SQL Server:“如果你查询那些SpecialOfferID<>1的行,这个索引有这些行的入口。”不管索引键是什么,让SQL Server知道这些信息都是有好处的。

想一想我们之前创建的一个索引,帮助仓库管理员查询SalesOrderDetail表中产品相关的信息。

 

 
  1. CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate 
  2.    ON Sales.SalesOrderDetail (ProductID,ModifiedDate) 
  3.    INCLUDE (OrderQty,UnitPrice,LineTotal) ; 

上面的索引产生的结果是。

:- Search Key Columns -:      : ---   Included Columns  ---:    :---   Bookmark   ---:

ProductID   ModifiedDate      OrderQty UnitPrice LineTotal      OrderId     DetailId
----------- ------------      -------- --------- ---------      ----------- ----------

Page n-1:

709         01 Feb 2002       1            5.70       5.70      45329       6392
709         01 May 2002       1            5.70       5.70      46047       8601
710         01 Jul 2001       1            5.70       5.70      43670       111
710         01 Jul 2001       1            5.70       5.70      43676       152
710         01 Sep 2001       1            5.70       5.70      44075       1448

Page n:

710         01 Oct 2001       1            5.70       5.70      44303       2481
710         01 Nov 2001       1            5.70       5.70      44484       2853
710         01 Nov 2001       1            5.70       5.70      44499       3006
710         01 Nov 2001       1            5.70       5.70      44523       3346
710         01 Nov 2001       1            5.70       5.70      44527       3400

如果仓库管理员经常查询SpecialOfferID<>1的信息,很少查询=1的信息,在创建索引的时候添加一个SpecialOfferID!=1的where子句是很有意义的。结果就是很小的索引就可以覆盖大部分的请求。

 

 
  1. CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate 
  2.   ON Sales.SalesOrderDetail (ProductID,ModifiedDate) 
  3.   INCLUDE (OrderQty,UnitPrice,LineTotal) 
  4.   WHERE SpecialOfferID <>1 

执行下面的查询语句。

 

 
  1. SELECT  ProductID , 
  2.         ModifiedDate , 
  3.         SUM(OrderQty) 'No of Items' , 
  4.         AVG(UnitPrice) 'Avg Price' , 
  5.         SUM(LineTotal) 'Total Value' 
  6. FROM    Sales.SalesOrderDetail 
  7. WHERE   SpecialOfferID <> 1 
  8. GROUP BY ProductID , 
  9.         ModifiedDate 

SQL Server管理器告诉我们,过滤的索引被扫描,读取了36页,产生2102条结果。

一些警告

在决定使用过滤索引的时候,要记住两个重要的问题。

1 SQL Server如何评估过滤的索引

你可能会很奇怪,把之前查询语句的where条件从SpecialOfferID<>1变成SpecialOfferID=2,就会防止SQL Server使用过滤的索引。这是因为SQL Server比较了select查询的where子句和create index的where子句,认为他们两个语法上是相等的,而不是比较逻辑的相等。因此,SQL Server没有意识到过滤的索引覆盖了查询。

另外,你不能通过复合的where子句,例如:where SpecialOfferID<>1 and SpecialOfferID=2来促使SQL Server使用过滤的索引。在后面的级别中,我们将会给出一些提示,教给你一些影响SQL Server选择索引的能力。现在,记住SQL Server在评估过滤索引的时候,做出的是语法的决定。

2 不要使用过滤的索引来弥补不好的数据库设计

在创建过滤索引的时候,不要创建索引来弥补违反三范式的数据库设计。

大部分违反三范式的数据库设计,主要是对于实体的子类型认识错误。看一下下面的一张表。

ProductID Description Type Price Author IssuesPerYear
(Primary Key)          
44E Roots Book 44.50 Alex Haley  
17J Time Periodical 18.00   52
22D Gift from the Sea Book 37.00 Anne Morrow Lindbergh  
18K National Geographic Periodical 38.00   12
78K Good Housekeeping Periodical 37.00   12

 

很容易看出表中包含两种类型的商品:Book书和Periodical期刊。只有书才有Author作者信息,只有期刊才有IssuesPerYear每年的刊数信息。正确的方法是,一张主表来存放公共的信息,然后每个子类型附件一张表。每张表都有相同的主键,子类型表的主键也是连接主表的外键。

Products Table

ProductID Description Price
(Primary Key)    
44E Roots 44.50
17J Time 18.00
22D Gift from the Sea 37.00
18K National Geography 38.00
78K Good Housekeeping 37.00

Books table

ProductID Author
(Primary Key and Foreign Key)  
44E Alex Haley
22D Anne Morrow Lindbergh

Periodicals table

ProductID IssuesPerYear
(Primary Key and Foreign Key)  
17J 52
18K 12
78K 12

像上面的情况,可以建立过滤的索引,来过滤NULL列。

但是正确的做法是重新定义表结构。应用开发者和开发工具不知道你设计的索引,他们只能看见你的表。如果表的结构不影响业务的结构,开发者将会尽力的构建和维护数据库之上的应用。

结论

过滤的索引消除了索引中无用的入口,产生的索引更小,更有利于查询。过滤的索引是通过在create index中指定where子句来实现的。在where子句中的列不同于索引键的列,也不同于include子句中的列。

如果一张表中的一个子集经常被访问,过滤的索引也能是一个覆盖的索引,也可以导致IO有一个相当大的减少。

不要将创建过滤索引作为正确设计数据库的替代选择。




本文转自 virusswb 51CTO博客,原文链接:http://blog.51cto.com/virusswb/986526,如需转载请自行联系原作者

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
8天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
51 10
|
28天前
|
SQL 存储 数据库
sql事务、视图和索引
sql事务、视图和索引
14 0
|
1月前
|
SQL 数据库 数据安全/隐私保护
Sql Server数据库Sa密码如何修改
Sql Server数据库Sa密码如何修改
|
18天前
|
SQL
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
16 0
|
8天前
|
SQL 算法 数据库
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
68 6
|
3天前
|
SQL 安全 网络安全
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
8 0
|
8天前
|
SQL 存储 数据挖掘
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
服务器数据恢复环境: 一台安装windows server操作系统的服务器。一组由8块硬盘组建的RAID5,划分LUN供这台服务器使用。 在windows服务器内装有SqlServer数据库。存储空间LUN划分了两个逻辑分区。 服务器故障&初检: 由于未知原因,Sql Server数据库文件丢失,丢失数据涉及到3个库,表的数量有3000左右。数据库文件丢失原因还没有查清楚,也不能确定数据存储位置。 数据库文件丢失后服务器仍处于开机状态,所幸没有大量数据写入。 将raid5中所有磁盘编号后取出,经过硬件工程师检测,没有发现明显的硬件故障。以只读方式将所有磁盘进行扇区级的全盘镜像,镜像完成后将所
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
|
10天前
|
SQL 数据库 索引
SQL索引失效原因分析与解决方案
SQL索引失效原因分析与解决方案
19 0
|
12天前
|
SQL 数据安全/隐私保护
SQL Server 2016安装教程
SQL Server 2016安装教程
17 1
|
12天前
|
SQL 安全 Java
SQL server 2017安装教程
SQL server 2017安装教程
14 1