like语句百分号前置会使用到索引吗?
like语句百分号前置会使用到索引吗?
前几天看了这篇文章:谈SQL Server对like '%关键词%' 处理时的索引利用问题
看完了之后,我很想知道这篇文章是不是临时工写的?还是网站的主人写的,网站的主人的微博我都有关注(在微博里私信过)
是某个公司的DBA,这里先不管他是不是临时工写的,今天我也研究一下这个问题o(∩_∩)o
说明:我们说的走索引指的是:聚集索引查找、非聚集索引查找
而全表扫描、聚集索引扫描、非聚集索引扫描都不是走索引
而这里说的走索引跟全文搜索/全文索引没有关系 SQLSERVER全文搜索
全文搜索/全文索引已经是另外一种技术了
聚集索引表
SQL脚本如下:
--聚集索引表
USE [pratice]
GO
CREATE TABLE Department(
DepartmentID int IDENTITY(1,1) NOT NULL ,
GroupName NVARCHAR(20) NOT NULL,
Company NVARCHAR(20),
) CREATE CLUSTERED INDEX CL_GroupName ON [dbo].[Department](GroupName ASC) DECLARE @i INT
SET @i=1
WHILE @i < 100000
BEGIN
INSERT INTO Department ( [Company], groupname )
VALUES ( '中国你好有限公司XX分公司'+CAST(@i AS VARCHAR(20)), '销售组'+CAST(@i AS VARCHAR(20)) )
SET @i = @i + 1
END SELECT * FROM Department
表数据
聚集索引创建在GROUPNAME这个字段上,我们就查找GROUPNAME这个字段
如果看过我以前写的文章的人肯定知道:查找只会出现在建立索引的时候的第一个字段(这里我就不再详细叙述了)
--聚集索引查找
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000'
--聚集索引扫描
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000%'
--聚集索引扫描
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000'
--聚集索引查找
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000%'
--------------------------------------------------------------------------
IO和时间统计
SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000%'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000%'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
(1 行受影响)LIKE '销售组1000'
表 'Department'。扫描计数 1,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 (11 行受影响)LIKE '%销售组1000%'
表 'Department'。扫描计数 1,逻辑读取 448 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 47 毫秒,占用时间 = 47 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 (1 行受影响)LIKE '%销售组1000'
表 'Department'。扫描计数 1,逻辑读取 448 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 47 毫秒,占用时间 = 40 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 (11 行受影响)LIKE '销售组1000%'
表 'Department'。扫描计数 1,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
只有LIKE '销售组1000'和LIKE '销售组1000%'用到了查找
为什麽?我会在文中最后说明
非聚集索引表
SQL脚本如下:
我们drop掉刚才的department表
DROP TABLE [dbo].[Department]
重新建立department表
--非聚集索引表
USE [pratice]
GO
CREATE TABLE Department(
DepartmentID int IDENTITY(1,1) NOT NULL ,
GroupName NVARCHAR(20) NOT NULL,
Company NVARCHAR(20),
) CREATE INDEX CL_GroupName ON [dbo].[Department](GroupName ASC) DECLARE @i INT
SET @i=1
WHILE @i < 100000
BEGIN
INSERT INTO Department ( [Company], groupname )
VALUES ( '中国你好有限公司XX分公司'+CAST(@i AS VARCHAR(20)), '销售组'+CAST(@i AS VARCHAR(20)) )
SET @i = @i + 1
END SELECT * FROM Department
非聚集索引依然建立在GROUPNAME这个字段上
表数据
同样,我们使用上面讲解聚集索引表时候的查询语句
--非聚集索引查找 RID查找
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000'
--非聚集索引扫描 RID查找
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000%'
--非聚集索引扫描 RID查找
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000'
--非聚集索引查找 RID查找
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000%'
因为是select * 所以SQLSERVER需要到数据页面去找其他的字段数据,使用到RID查找
这里的结果跟聚集索引是一样的
-------------------------------------------------------------------------------------------
IO和时间统计
SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000%'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000%'
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
(1 行受影响) LIKE '销售组1000'
表 'Department'。扫描计数 1,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 62 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 (11 行受影响)LIKE '%销售组1000%'
表 'Department'。扫描计数 1,逻辑读取 92 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 16 毫秒,占用时间 = 17 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 (1 行受影响)LIKE '%销售组1000'
表 'Department'。扫描计数 1,逻辑读取 82 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 15 毫秒,占用时间 = 17 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 (11 行受影响)LIKE '销售组1000%'
表 'Department'。扫描计数 1,逻辑读取 13 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。 SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
为什麽只有LIKE '销售组1000'和LIKE '销售组1000%'用到了查找???
如果阁下曾经有看过我写的
SQLSERVER聚集索引与非聚集索引的再次研究(上)
SQLSERVER聚集索引与非聚集索引的再次研究(下)
您就会知道在聚集索引页面和非聚集索引页面里都有一个KeyHashValue的字段
聚集索引页面
非聚集索引页面
当使用 '%销售组1000%'和'%销售组1000'的时候,因为是模糊匹配(百分号前置)
SQLSERVER不会去匹配hash值(KeyHashValue),直接扫描(SCAN)算了
但是使用'销售组1000'和'销售组1000%'的时候
'销售组1000' :SQLSERVER能够准确匹配到唯一的一个hash值
'销售组1000%':SQLSERVER会匹配与销售组1000相同的hash值
与销售组1000%匹配的记录会有多个,所以逻辑读取次数也会有多次
所以,'销售组1000'和'销售组1000%'能够使用查找(SEEK)
总结
只有了解了SQLSERVER的内部原理,才能够明白更多
注意:我这里并没有将非聚集索引扫描纳入到“走索引”这个分类,如果将非聚集索引扫描纳入到“走索引”这个分类里
那么我的朋友的文章就是对的,随便加个非聚集索引,让表扫描/聚集索引扫描变成非聚集索引扫描,就认为是走索引
(虽然非聚集索引扫描比聚集索引扫描/表扫描快,IO少)
那么下面四个语句都是属于走索引,没有什么好讨论的,我们讨论的前提是在基础表上不加任何东西,如果在做实验的过程中
随便加个非聚集索引,然后走非聚集索引扫描就说走索引,那么这篇文章就没有意义了,经过再三斟酌,
我决定将“非聚集索引扫描”移出“走索引”这个分类,毕竟查找(SEEK)比扫描(SCAN)快
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000'
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000%'
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%销售组1000'
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '销售组1000%'
最最后,补充说一下
我们判断一个执行计划的性能的好坏的标准是什么??
就是哪个执行计划的逻辑读次数最少
Logical reads:包含该语句从内存数据缓冲区中访问的页数和从物理磁盘读取的页数。
如果全表扫描/聚集索引扫描所使用的逻辑读比聚集索引查找/非聚集索引查找使用的逻辑读要少,
或者全表扫描比非聚集索引扫描使用的逻辑读要少
那么SQLSERVER选择全表扫描/聚集索引扫描这个执行计划就是好的
有些人为了让SQLSERVER使用索引,不惜代价使用查询提示,让SQLSRVER去走索引,这样是得不偿失的
我们的最终目的是:减少逻辑读次数,不要为了索引而索引!!
当然,我这里的实验环境跟各位的真实环境会有差别,不过“逻辑读次数”这个标准无论是哪个环境都是一样的!!
我说完了,谢谢大家o(∩_∩)o
physical reads:表示那些没有驻留在内存缓冲区中需要从磁盘读取的数据页。
Read-ahead reads是SQL Server为了提高性能而产生的预读。
如有不对的地方,欢迎大家拍砖o(∩_∩)o
like语句百分号前置会使用到索引吗?的更多相关文章
- 【转】同一个SQL查询语句,为什么使用到的索引不同?
问: 同一个SQL查询语句,只是修改where条件中的一个值,为什么使用到的索引情况也会不同?谢谢! 1) explain执行结果,如下图: 2) 表中的数据如下图: 3) 表结构如下图: 4) 创建 ...
- 【测试】并使用scott用户下的emp表写一条SQL语句,执行计划走唯一索引
SQL; SAL ---------- Execution Plan ---------------------------------------------------------- ------ ...
- 我的MYSQL学习心得(十六) 优化
我的MYSQL学习心得(十六) 优化 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...
- MYSQL学习心得 优化
这一篇主要介绍MYSQL的优化,优化MYSQL数据库是DBA和开发人员的必备技能 MYSQL优化一方面是找出系统瓶颈,提高MYSQL数据库整体性能:另一方面需要合理的结构设计和参数调整,以提高 用户操 ...
- 应用索引技术优化SQL 语句(转)
原文出处 一.前言 很多数据库系统性能不理想是因为系统没有经过整体优化,存在大量性能低下的SQL 语句.这类SQL语句性能不好的首要原因是缺乏高效的索引.没有索引除了导致语句本身运行速度慢外,更是导致 ...
- SQL语句优化、mysql不走索引的原因、数据库索引的设计原则
SQL语句优化 1 企业SQL优化思路 1.把一个大的不使用索引的SQL语句按照功能进行拆分 2.长的SQL语句无法使用索引,能不能变成2条短的SQL语句让它分别使用上索引. 3.对SQL语句功能的拆 ...
- SQL语句-创建索引
语法:CREATE [索引类型] INDEX 索引名称ON 表名(列名)WITH FILLFACTOR = 填充因子值0~100 GO USE 库名GO IF EXISTS (SELECT * FRO ...
- SQL Server调优系列玩转篇三(利用索引提示(Hint)引导语句最大优化运行)
前言 本篇继续玩转模块的内容,关于索引在SQL Server的位置无须多言,本篇将分析如何利用Hint引导语句充分利用索引进行运行,同样,还是希望扎实掌握前面一系列的内容,才进入本模块的内容分析. 闲 ...
- 如何使用CREATE INDEX语句对表增加索引?
创建和删除索引索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER TABLE来给表增加索引.删除索引可以利用ALTER TABLE或DROP INDE ...
随机推荐
- linux环境中使用转义字符使命令行字符颜色高亮
而通过转义序列设置终端显示属性的格式为: \033[Param {;Param;...}m 其中转义序列以 \033[ 为开头,m 为设置属性结束,中间部分的 Param 为属性值,{} 表示可以设置 ...
- UVA 11768 Lattice Point or Not(扩展欧几里德)
将直线转化为ax + by = c的形式,然后扩展欧几里得求在[x1, x2]之间的解 对直线与坐标轴平行的特判 调试了好长时间,注意: 1 正负数转化为整型的处理 2 注意判断有无解 #includ ...
- spring boot使用
首先spring-boot是个服务框架,更加准确来讲是个微服务框架,实际上来说”微“并不“微”,spring-boot包含很多可嵌入的组件,通过这些组件可以来完成我们的服务, 以往我们使用Spring ...
- Windows下搭建Wordpress博客网站
一:安装wamp Windows下的Apache+Mysql/MariaDB+Perl/PHP/Python,一组常用来搭建动态网站或者服务器的开源软件,本身都是各自独立的程序,但是因为常被放在一起使 ...
- 安装openssl 扩展的时候出现Cannot find config.m4. Make sure that you run '/usr/local/php/bin/phpize' in the top level source directory of the module的解决方法
进入php源码包目录:cd /usr/local/php-5.6.25/ext/openssl 执行命令: cp ./config0.m4 ./config.m4 即可
- CVE-2010-3654分析及利用
三年前分析的一个漏洞,最近又温习一遍,这个flash中混淆漏洞的鼻祖,10年最经典的漏洞. 漏洞触发原因 该漏洞主要因为avm对返回的类没有进行校验,通过修改swf文件,实现Ref类和Origin类的 ...
- iOS 图片文件格式判断、圆角图片
1.圆角图片 // 设置圆形图片(放到分类中使用) - (UIImage *)cutCircleImage { UIGraphicsBeginImageContextWithOptions(self. ...
- window10 office 手工完全卸载
在地址栏输入itellyou,点击第一个搜索结果,可以从微软官方网站下载office安装. 一下是一点需要注意到的地方: 本次安装的是office2016其它类似 下载解压有的目录结构: 如果你是x6 ...
- Ubuntu 树莓派2b交叉编译环境
在一个平台上生成另一个平台上的可执行代码.为什么要大费周折的进行交叉编译呢?一句话:不得已而为之.有时是因为目的平台上不允许或不能够安装所需要的编译器,而又需要这个编译器的某些特征:有时是因为目的平台 ...
- [ACM训练] ACM中巧用文件的输入输出来改写acm程序的输入输出 + ACM中八大输入输出格式
ACM中巧用文件的输入输出来改写acm程序的输入输出 经常有见大神们使用文件来代替ACM程序中的IO,尤其是当程序IO比较复杂时,可以使自己能够更专注于代码的测试,而不是怎样敲输入. C/C++代码中 ...