[转帖]SQL SERVER中什么情况会导致索引查找变成索引扫描
https://www.cnblogs.com/kerrycode/p/4806236.html
SQL Server 中什么情况会导致其执行计划从索引查找(Index Seek)变成索引扫描(Index Scan)呢? 下面从几个方面结合上下文具体场景做了下测试、总结、归纳。
1:隐式转换会导致执行计划从索引查找(Index Seek)变为索引扫描(Index Scan)
Implicit Conversion will cause index scan instead of index seek. While implicit conversions occur in SQL Server to allow data evaluations against different data types, they can introduce performance problems for specific data type conversions that result in an index scan occurring during the execution. Good design practices and code reviews can easily prevent implicit conversion issues from ever occurring in your design or workload.
如下示例,AdventureWorks2014数据库的HumanResources.Employee表,由于NationalIDNumber字段类型为NVARCHAR,下面SQL发生了隐式转换,导致其走索引扫描(Index Scan)
SELECT NationalIDNumber, LoginID
FROM HumanResources.Employee
WHERE NationalIDNumber = 112457891

我们可以通过两种方式避免SQL做隐式转换:
1:确保比较的两者具有相同的数据类型。
2:使用强制转换(explicit conversion)方式。
我们通过确保比较的两者数据类型相同后,就可以让SQL走索引查找(Index Seek),如下所示
SELECT nationalidnumber,
loginid
FROM humanresources.employee
WHERE nationalidnumber = N'112457891'

注意:并不是所有的隐式转换都会导致索引查找(Index Seek)变成索引扫描(Index Scan),Implicit Conversions that cause Index Scans 博客里面介绍了那些数据类型之间的隐式转换才会导致索引扫描(Index Scan)。如下图所示,在此不做过多介绍。


避免隐式转换的一些措施与方法
1:良好的设计和代码规范(前期)
2:对发布脚本进行Review(中期)
3:通过脚本查询隐式转换的SQL(后期)
下面是在数据库从执行计划中搜索隐式转换的SQL语句
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE @dbname SYSNAME
SET @dbname = QUOTENAME(DB_NAME());
WITH XMLNAMESPACES
(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
stmt.value('(@StatementText)[1]', 'varchar(max)'),
t.value('(ScalarOperator/Identifier/ColumnReference/@Schema)[1]', 'varchar(128)'),
t.value('(ScalarOperator/Identifier/ColumnReference/@Table)[1]', 'varchar(128)'),
t.value('(ScalarOperator/Identifier/ColumnReference/@Column)[1]', 'varchar(128)'),
ic.DATA_TYPE AS ConvertFrom,
ic.CHARACTER_MAXIMUM_LENGTH AS ConvertFromLength,
t.value('(@DataType)[1]', 'varchar(128)') AS ConvertTo,
t.value('(@Length)[1]', 'int') AS ConvertToLength,
query_plan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp
CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
CROSS APPLY stmt.nodes('.//Convert[@Implicit="1"]') AS n(t)
JOIN INFORMATION_SCHEMA.COLUMNS AS ic
ON QUOTENAME(ic.TABLE_SCHEMA) = t.value('(ScalarOperator/Identifier/ColumnReference/@Schema)[1]', 'varchar(128)')
AND QUOTENAME(ic.TABLE_NAME) = t.value('(ScalarOperator/Identifier/ColumnReference/@Table)[1]', 'varchar(128)')
AND ic.COLUMN_NAME = t.value('(ScalarOperator/Identifier/ColumnReference/@Column)[1]', 'varchar(128)')
WHERE t.exist('ScalarOperator/Identifier/ColumnReference[@Database=sql:variable("@dbname")][@Schema!="[sys]"]') = 1
2:非SARG谓词会导致执行计划从索引查找(Index Seek)变为索引扫描(Index Scan)
SARG(Searchable Arguments)又叫查询参数, 它的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值的范围内的匹配或者两个以上条件的AND连接。不满足SARG形式的语句最典型的情况就是包括非操作符的语句,如:NOT、!=、<>;、!<;、!>;NOT EXISTS、NOT IN、NOT LIKE等,另外还有像在谓词使用函数、谓词进行运算等。
2.1:索引字段使用函数会导致索引扫描(Index Scan)
SELECT nationalidnumber,
loginid
FROM humanresources.employee
WHERE SUBSTRING(nationalidnumber,1,3) = '112'

2.2索引字段进行运算会导致索引扫描(Index Scan)
对索引字段字段进行运算会导致执行计划从索引查找(Index Seek)变成索引扫描(Index Scan):
SELECT * FROM Person.Person WHERE BusinessEntityID + 10 < 260

一般要尽量避免这种情况出现,如果可以的话,尽量对SQL进行逻辑转换(如下所示)。虽然这个例子看起来很简单,但是在实际中,还是见过许多这样的案例,就像很多人知道抽烟有害健康,但是就是戒不掉!很多人可能了解这个,但是在实际操作中还是一直会犯这个错误。道理就是如此!
SELECT * FROM Person.Person WHERE BusinessEntityID < 250

2.3 LIKE模糊查询回导致索引扫描(Index Scan)
Like语句是否属于SARG取决于所使用的通配符的类型, LIKE 'Condition%' 就属于SARG、LIKE ’%Condition'就属于非SARG谓词操作
SELECT * FROM Person.Person WHERE LastName LIKE 'Ma%'

SELECT * FROM Person.Person WHERE LastName LIKE '%Ma%'

3:SQL查询返回数据页(Pages)达到了临界点(Tipping Point)会导致索引扫描(Index Scan)或表扫描(Table Scan)
What is the tipping point?
It's the point where the number of rows returned is "no longer selective enough". SQL Server chooses NOT to use the nonclustered index to look up the corresponding data rows and instead performs a table scan.
关于临界点(Tipping Point),我们下面先不纠结概念了,先从一个鲜活的例子开始吧:
SET NOCOUNT ON;
DROP TABLE TEST
CREATE TABLE TEST (OBJECT_ID INT, NAME VARCHAR(8));
CREATE INDEX PK_TEST ON TEST(OBJECT_ID)
DECLARE @Index INT =1;
WHILE @Index <= 10000
BEGIN
INSERT INTO TEST
SELECT @Index, 'kerry';
SET @Index = @Index +1;
END
UPDATE STATISTICS TEST WITH FULLSCAN;
SELECT * FROM TEST WHERE OBJECT_ID= 1
如上所示,当我们查询OBJECT_ID=1的数据时,优化器使用索引查找(Index Seek)

上面OBJECT_ID=1的数据只有一条,如果OBJECT_ID=1的数据达到全表总数据量的20%会怎么样? 我们可以手工更新2001条数据。此时SQL的执行计划变成全表扫描(Table Scan)了。
UPDATE TEST SET OBJECT_ID =1 WHERE OBJECT_ID<=2000;
UPDATE STATISTICS TEST WITH FULLSCAN;
SELECT * FROM TEST WHERE OBJECT_ID= 1


临界点决定了SQL Server是使用书签查找还是全表/索引扫描。这也意味着临界点只与非覆盖、非聚集索引有关(重点)。
Why is the tipping point interesting?
It shows that narrow (non-covering) nonclustered indexes have fewer uses than often expected (just because a query has a column in the WHERE clause doesn't mean that SQL Server's going to use that index)
It happens at a point that's typically MUCH earlier than expected… and, in fact, sometimes this is a VERY bad thing!
Only nonclustered indexes that do not cover a query have a tipping point. Covering indexes don't have this same issue (which further proves why they're so important for performance tuning)
You might find larger tables/queries performing table scans when in fact, it might be better to use a nonclustered index. How do you know, how do you test, how do you hint and/or force… and, is that a good thing?
4:统计信息缺失或不正确会导致索引扫描(Index Scan)
统计信息缺失或不正确,很容易导致索引查找(Index Seek)变成索引扫描(Index Scan)。 这个倒是很容易理解,但是构造这样的案例比较难,一时没有想到,在此略过。
5:谓词不是联合索引的第一列会导致索引扫描(Index Scan)
SELECT * INTO Sales.SalesOrderDetail_Tmp FROM Sales.SalesOrderDetail;
CREATE INDEX PK_SalesOrderDetail_Tmp ON Sales.SalesOrderDetail_Tmp(SalesOrderID, SalesOrderDetailID);
UPDATE STATISTICS Sales.SalesOrderDetail_Tmp WITH FULLSCAN;
下面这个SQL语句得到的结果是一致的,但是第二个SQL语句由于谓词不是联合索引第一列,导致索引扫描
SELECT * FROM Sales.SalesOrderDetail_Tmp
WHERE SalesOrderID=43659 AND SalesOrderDetailID<10

SELECT * FROM Sales.SalesOrderDetail_Tmp WHERE SalesOrderDetailID<10

参考资料:
https://www.sqlskills.com/blogs/jonathan/implicit-conversions-that-cause-index-scans/
http://stackoverflow.com/questions/6528906/why-is-this-an-index-scan-and-not-a-index-seek
http://pramodsingla.com/2011/05/16/cause-of-index-scan/
http://stackoverflow.com/questions/6528906/why-is-this-an-index-scan-and-not-a-index-seek
http://www.sqlskills.com/blogs/kimberly/the-tipping-point-query-answers/
[转帖]SQL SERVER中什么情况会导致索引查找变成索引扫描的更多相关文章
- SQL SERVER中什么情况会导致索引查找变成索引扫描
SQL Server 中什么情况会导致其执行计划从索引查找(Index Seek)变成索引扫描(Index Scan)呢? 下面从几个方面结合上下文具体场景做了下测试.总结.归纳. 1:隐式转换会导致 ...
- SQL Server中TOP子句可能导致的问题以及解决办法
简介 在SQL Server中,针对复杂查询使用TOP子句可能会出现对性能的影响,这种影响可能是好的影响,也可能是坏的影响,针对不同的情况有不同的可能性. 关系数据库中SQL语句只 ...
- c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
- SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析
在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...
- SQL Server中的联合主键、聚集索引、非聚集索引、mysql 联合索引
我们都知道在一个表中当需要2列以上才能确定记录的唯一性的时候,就需要用到联合主键,当建立联合主键以后,在查询数据的时候性能就会有很大的提升,不过并不是对联合主键的任何列单独查询的时候性能都会提升,但我 ...
- SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析 (转载)
在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...
- SQL Server中的联合主键、聚集索引、非聚集索引
我们都知道在一个表中当需要2列以上才能确定记录的唯一性的时候,就需要用到联合主键,当建立联合主键以后,在查询数据的时候性能就会有很大的提升,不过并不是对联合主键的任何列单独查询的时候性能都会提升,但我 ...
- 理解SQL Server中索引的概念
T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他 简介 在SQL Server中,索引是一种增强式的存在,这意味着,即使没有索引,SQL Server仍然可以实现应有的功能 ...
- T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他
简介 在SQL Server中,索引是一种增强式的存在,这意味着,即使没有索引,SQL Server仍然可以实现应有的功能.但索引可以在大多数情况下大大提升查询性能,在OLAP中尤其明显.要完全理解索 ...
- 理解SQL Server中索引的概念,原理
转自:http://www.cnblogs.com/CareySon/archive/2011/12/22/2297568.html 简介 在SQL Server中,索引是一种增强式的存在,这意味着, ...
随机推荐
- libGDX游戏开发之Box2D碰撞(十五)
libGDX游戏开发之Box2D(十四) libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网和goo ...
- Baby_Step_Gaint_Step(BSGS) 算法
\(BSGS\) 算法,又称 "北(\(B\))上(\(S\))广(\(G\))深(\(S\))" 算法,"拔山盖世"算法,可以在 \(O(\sqrt{n})\ ...
- NSSCTF Round#16 Basic crypto misc-wp
CRYPTO pr 题目 CRT from Crypto.Util.number import * import random flag=plaintext = 'NSSCTF{*********** ...
- Multi-Architecture镜像制作指南已到,请查收!
摘要:使用Multi-Architecture镜像,可以让docker根据系统架构去拉取对应的镜像,服务的部署脚本等可以在不同架构的系统间使用相同的配置,减化服务配置,提高了服务在不同系统架构间的一致 ...
- 用了这个评估优化LiteOS镜像利器,我有点飘...
摘要:本文会给大家介绍下LiteOS Studio的镜像分析工具,这可是一个评估.优化镜像文件RAM.ROM占用大小的利器. 大家都知道嵌入式开发板由于受成本限制,芯片的RAM.Flash等硬件资源有 ...
- 云小课|三大灵魂拷问GaussDB(DWS)数据落盘安全问题
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:GaussDB(D ...
- 移动应用中的第三方SDK隐私合规检测,早知道
摘要: 在移动应用隐私合规检测中,第三方SDK隐私声明由于其展现位置展现形式的多样性,自动化提取与解析是比较困难的任务. 本文分享自华为云社区<移动应用中的第三方SDK隐私合规检测>,作者 ...
- A/B 实验避坑指南:为什么不建议开 AABB 实验
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 本文将针对日常开设 A/B 实验过程中一个不太合理的使用方法--AABB 实验进行详细的解释,告诉大家为什么不建议 ...
- 这两种完全不同的JPEG加载方式,你肯定见过!
现如今网站所使用的的图片格式多种多样,但是有一种图片格式占到了 74% 的使用量.它就是 JPEG,即联合图像专家组.这类文件的后缀通常为 .jpg 或 .jpeg,是摄影中常见的图片类型. JPEG ...
- 【JAVA基础】报错解决
报错解决 Failed to configure a DataSource: 'url' attribute is not specified and no embedd https://change ...