SqlServer(索引)--创建复合索引时,复合索引列顺序对查询的性能影响[转]

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
简介:

http://www.cnblogs.com/wy123/p/5604400.html
SQL Server创建复合索引时,复合索引列顺序对查询的性能影响

说说复合索引
写索引的博客太多了,一直不想动手写,有一下两个原因:
一是觉得有炒剩饭的嫌疑,有兄弟曾说:索引吗,只要在查询条件上建索引就行了,真的可以这么暴力吗?
二来觉得,索引是个非常大的话题,很难概括出所有的情况,你不整出点新意来,倒是有抄袭照搬的嫌疑
既然写了,就写一点稍微不一样的东西出来,
好了,废话打住,
/*
  20160814备注:今天发现一个类似的文章:http://www.cnblogs.com/fly_zj/archive/2012/08/11/2633629.html
  可以理解为:添加组合索引时,做相等运算字段应该放在最前面
  但这么说也不完全准确,应该说是,将选择性高的字段,放在最前面,通俗说就是,将最有效的过滤条件,方式复合索引的第一位
*/

搭建测试环境:
创建一张表,模拟实际业务中的一个表,往里面填入数据,时间字段上,相对按照时间均匀地填充,其他字段以GUID填充

Create table BusinessInfoTable
(
    BuniessCode1 varchar(50),
    BuniessCode2 varchar(50),
    BuniessCode3 varchar(50),
    BuniessCode4 varchar(50),
    BuniessStatus1 tinyint,
    BuniessStatus2 tinyint,
    BuniessDateTime1 Datetime,
    BuniessDateTime2 Datetime,
    OtherColumn1 varchar(50),
    OtherColumn2 varchar(50),
    OtherColumn3 varchar(50)
)

declare @i int=0
while @i<1000000
begin
    insert into BusinessInfoTable 
    values 
    (
        NEWID(),NEWID(),NEWID(),NEWID(),RAND()*100,RAND()*100,
        DATEADD(MI,@i,GETDATE()),DATEADD(MI,@i,GETDATE()),NEWID(),NEWID(),NEWID()
    )
    set @i=@i+1
end

现在有这么一个查询(实际上查询远比这个复杂,简化一点,不要刻意造环境)

DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
SET @startDate = CONVERT(CHAR(10), GETDATE(), 120);
SET @endDate = DATEADD(dd, 7, @startDate);
SELECT OtherColumn2,
     BuniessStatus1,
     BuniessStatus2,
     BuniessDateTime1,
     BuniessDateTime2
FROM BusinessInfoTable
WHERE BuniessDateTime1 BETWEEN @startDate AND @endDate
    AND BuniessDateTime2 BETWEEN @startDate AND @endDate
    AND BuniessStatus1 = 55
    AND BuniessStatus2 = 66;

1

说明一点:
暂时不考虑聚集索引,毕竟一个表上只能有一个聚集索引,
别人也不是傻子,不会轻易去建聚集索引,聚集索引早被占用了
既然被占用了,原则是一般不去动别人现有的东西的,比如别人建了聚集索引,你给人家删了,根据自己的情况建聚集索引,这不是找*么

有经验的你一定考虑符合索引了,同时考虑到为避免Key Lookup导致的书签查找,我们把查询索要的OtherColumn2列include进来
比如这样

CREATE NONCLUSTERED INDEX IDX_1 ON BusinessInfoTable 
**(BuniessStatus1,BuniessStatus2,BuniessDateTime1,BuniessDateTime2)**
INCLUDE(OtherColumn2)

或者这样,只是索引前导列顺序不一样

CREATE NONCLUSTERED INDEX IDX_2 ON BusinessInfoTable
**(BuniessDateTime1,BuniessDateTime2,BuniessStatus1,BuniessStatus2)**
INCLUDE(OtherColumn2)

  当然可以随意调整四个列的顺序,我就不过多地做演示了,有兴趣的自己试
  这里的前导列的顺序并不会影响到索引的使用,查询的时候都是非聚集索引Seek,绝对的
  那么问题来了,完全一样的查询条件,结果一样,使用不同的索引,索引的区别仅仅是列顺序不一样,其代价一样吗,有区别吗?

  同样查询,使用不同索引的结果(分别是上面的IDX_1和IDX_2):
  下面看图说话

DECLARE @startDate DATETIME;
DECLARE @endDate DATETIME;
SET @startDate = CONVERT(CHAR(10), GETDATE(), 120);
SET @endDate = DATEADD(dd, 7, @startDate);
SELECT OtherColumn2,
       BuniessStatus1,
       BuniessStatus2,
       BuniessDateTime1,
       BuniessDateTime2
FROM BusinessInfoTable WITH(INDEX(IDX_1))
WHERE BuniessDateTime1 BETWEEN @startDate AND @endDate
      AND BuniessDateTime2 BETWEEN @startDate AND @endDate
      AND BuniessStatus1 = 55
      AND BuniessStatus2 = 66;

     SELECT OtherColumn2,
       BuniessStatus1,
       BuniessStatus2,
       BuniessDateTime1,
       BuniessDateTime2
FROM BusinessInfoTable WITH(INDEX(IDX_2))
WHERE BuniessDateTime1 BETWEEN @startDate AND @endDate
      AND BuniessDateTime2 BETWEEN @startDate AND @endDate
      AND BuniessStatus1 = 55
      AND BuniessStatus2 = 66;

2
看看IO情况

3

4

原因分析:
  看来是有点差别吧,好似乎这个差别还真不小。
  究竟原因在何?
  索引是以平衡树(B树)的方式存在的,复合索引的列的顺序决定了B树的信息的存储的顺序
  如果是以BuniessStatus1列为前导列,因为BuniessStatus1分布的范围(相对)较小,
  这样在查询的时候通过BuniessStatus1=55就可以过滤出来一个比较小的结果集,后面依次用其他条件过滤就相对较快了
  比如BuniessStatus1=55过滤出来符合条件的数据有5条,加上BuniessStatus2 BuniessDateTime1 BuniessDateTime2 这三个条件再过滤,出来一条数据。
  如果BuniessDateTime1 是索引的前导列,用BuniessDateTime1 between @startDate and @endDate 过滤 ,可能会有10000条数据,
  然后依次再用 BuniessDateTime2,BuniessStatus1, BuniessStatus2过滤,最后也只有一条符合条件的数据。
  差别就在于:一开始的过滤条件,决定了查询多少page初步确定满足条件的数据,再进一步的进行过滤
  如果最开始就相对精确地确定了满足查询条件的数据范围,后面可以通过相对较小的代价来最终确认出满足条件的数据
  如果最开始相对模糊地却确定了满足查询条件的数据范围,那么这个过程的代价就相对比较大,虽然后面通过每一个条件的过,结果是一样的
  当然这种索引的建立跟数据分布有关,但是,这里没有下结论说,复合索引一定要按照什么什么顺序来是最好的
  还是那句话:具体问题具体分析,避免经验主义,没有一刀切的手段可以解决所有的问题。

总结:
  本文通过一个简单的例子,分析了创建符合索引时,列的顺序对查询的影响,
  说明在创建索引的时候,不仅仅要考虑在哪些列上创建索引,同时也要注意到,索引列的顺序,是否会对查询产生影响。
  避免一说到索引,就是“在查询条件上建索引”的暴力做法。

相关实践学习
使用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
目录
相关文章
|
2月前
|
SQL 存储 数据挖掘
SQL Server 日期格式查询详解
SQL Server 日期格式查询详解
169 2
|
3月前
|
SQL 存储 索引
SQL Server的Descending Indexes降序索引
【9月更文挑战第21天】在SQL Server中,降序索引允许指定列的排序顺序为降序,可显著优化涉及降序排序的查询性能,特别是在复合索引中。通过创建降序索引,可以更高效地满足特定业务需求,如按交易时间降序获取最新记录。然而,使用时需考虑查询频率、数据分布及维护成本,以确保最佳性能。
|
4月前
|
SQL Java 数据库
jsp中使用Servlet查询SQLSERVER数据库中的表的信息,并且打印在屏幕上
该博客文章介绍了在JSP应用中使用Servlet查询SQL Server数据库的表信息,并通过JavaBean封装图书信息,将查询结果展示在Web页面上的方法。
jsp中使用Servlet查询SQLSERVER数据库中的表的信息,并且打印在屏幕上
|
4月前
|
SQL Java 数据库连接
JDBC连接SQL Server2008 完成增加、删除、查询、修改等基本信息基本格式及示例代码
这篇文章提供了使用JDBC连接SQL Server 2008数据库进行增加、删除、查询和修改操作的基本步骤和示例代码。
|
4月前
|
SQL 存储 安全
Play Framework的安全面纱:揭开隐藏在优雅代码下的威胁
【8月更文挑战第31天】Play Framework 是一款高效、轻量级的 Web 开发框架,内置多种安全特性,助力开发者构建安全稳定的应用。本文详细介绍 Play 如何防范 SQL 注入、XSS 攻击、CSRF 攻击,并提供安全的密码存储方法及权限管理策略,通过具体示例代码展示实施步骤,助您有效抵御常见威胁。
68 0
|
4月前
|
SQL 存储 开发框架
Entity Framework Core 与 SQL Server 携手,高级查询技巧大揭秘!让你的数据操作更高效!
【8月更文挑战第31天】Entity Framework Core (EF Core) 是一个强大的对象关系映射(ORM)框架,尤其与 SQL Server 数据库结合使用时,提供了多种高级查询技巧,显著提升数据操作效率。它支持 LINQ 查询,使代码简洁易读;延迟加载与预先加载机制优化了相关实体的加载策略;通过 `FromSqlRaw` 或 `FromSqlInterpolated` 方法支持原始 SQL 查询;可调用存储过程执行复杂任务;利用 `Skip` 和 `Take` 实现分页查询,便于处理大量数据。这些特性共同提升了开发者的生产力和应用程序的性能。
203 0
|
4月前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
446 0
|
4月前
|
SQL 存储 测试技术
SQL Server 查询超时问题排查
【8月更文挑战第14天】遇到SQL Server查询超时,先检查查询复杂度与索引使用;审视服务器CPU、内存及磁盘I/O负载;审查SQL Server配置与超时设置;检测锁和阻塞状况;最后审查应用代码与网络环境。每步定位问题根源,针对性优化以提升查询效率。务必先行备份并在测试环境验证改动。
335 0
|
4月前
|
SQL 存储 关系型数据库
SQL SERVER 查询所有表 统计每张表的大小
SQL SERVER 查询所有表 统计每张表的大小
47 0
|
4月前
|
SQL
SQL SERVER 查询表结构,导出到Excel 生成代码用
SQL SERVER 查询表结构,导出到Excel 生成代码用
46 0