设为首页 收藏本站
查看: 871|回复: 0

[经验分享] SQL Server [索引查找/扫描] -- 什么情况会导致索引查找变成索引扫描

[复制链接]
发表于 2018-10-21 08:03:51 | 显示全部楼层 |阅读模式
  From: http://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, LoginIDFROM HumanResources.EmployeeWHERE NationalIDNumber = 112457891
  我们可以通过两种方式避免SQL做隐式转换:
  1:确保比较的两者具有相同的数据类型。
  2:使用强制转换(explicit conversion)方式。
  我们通过确保比较的两者数据类型相同后,就可以让SQL走索引查找(Index Seek),如下所示
SELECT nationalidnumber,       loginidFROM   humanresources.employeeWHERE  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 UNCOMMITTEDDECLARE @dbname SYSNAMESET @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_planFROM sys.dm_exec_cached_plans AS cpCROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qpCROSS 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,       loginidFROM   humanresources.employeeWHERE  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 TESTCREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(8));CREATE INDEX PK_TEST ON TEST(OBJECT_ID)DECLARE @Index INT =1;WHILE @Index

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-624277-1-1.html 上篇帖子: SQL版的UML活动图——Merge1.0 下篇帖子: 解决"The server quit without updating PID file."
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表