引用:初探Sql Server 执行计划及Sql查询优化

系统 1701 0
原文: 引用:初探Sql Server 执行计划及Sql查询优化

初探Sql Server 执行计划及Sql查询优化  收藏

MSSQL 优化之 ———— 探索 MSSQL 执行计划

 

作者: no_mIss

 

最近总想整理下对 MSSQL 的一些理解与感悟,却一直没有心思和时间写 ,晚上无事便写了一篇探索 MSSQL 执行计划,本文讲执行计划但不仅限于讲执行计划。  

网上的 SQL 优化的文章实在是很多,说实在的,我也曾经到处找这样的文章,什么不要使用 IN 了,什么 OR 了,什么 AND 了,很多很多,还有很多人拿出仅几 S 甚至几 MS 的时间差的例子来证明着什么 ( 有点可笑 ) ,让许多人不知道其是对还是错。而 SQL 优化又是每个要与数据库打交道的程序员的必修课,所以写了此文,与朋友们共勉。  

谈到优化就必然要涉及索引,就像要讲锁必然要说事务一样,所以你需要了解一下索引,仅仅是索引,就能讲半天了,所以索引我就不说了 ( 打很多字是很累的,况且我也知之甚少 ) ,可以去参考相关的文章,这个网上资料比较多了。  

今天来探索下 MSSQL 的执行计划,来让大家知道如何查看 MSSQL 的优化机制,以此来优化 SQL 查询。

 

--DROP TABLE T_UserInfo----------------------------------------------------

-- 建测试表

CREATE   TABLE  T_UserInfo

(

     Userid varchar ( 20 ),    UserName varchar ( 20 ),

     RegTime  datetime ,   Tel varchar ( 20 ),

)

-- 插入测试数据

DECLARE  @I  INT

DECLARE  @ENDID  INT

SELECT  @I  =  1

SELECT  @ENDID = 100    -- 在此处更改要插入的数据,重新插入之前要删掉所有数据

WHILE  @I  <=  @ENDID

BEGIN

     INSERT   INTO  T_UserInfo

     SELECT   'ABCDE' + CAST ( @I  AS  VARCHAR ( 20 ))+ 'EF' , ' ' + CAST ( @I  AS  VARCHAR ( 20 )),

        GETDATE (), '876543' + CAST ( @I  AS  VARCHAR ( 20 ))

     SELECT  @I  =  @I  +  1

END

 

-- 相关 SQL 语句解释

---------------------------------------------------------------------------

-- 建聚集索引

CREATE   CLUSTERED   INDEX  INDEX_Userid    ON  T_UserInfo  ( Userid )

-- 建非聚集索引

CREATE   NONCLUSTERED   INDEX  INDEX_Userid    ON  T_UserInfo  ( Userid )

-- 删除索引

DROP   INDEX  T_UserInfo . INDEX_Userid

---------------------------------------------------------------------------

---------------------------------------------------------------------------

-- 显示有关由 Transact-SQL  语句生成的磁盘活动量的信息

SET   STATISTICS  IO  ON

-- 关闭有关由 Transact-SQL  语句生成的磁盘活动量的信息

SET   STATISTICS  IO  OFF

-- 显示 [ 返回有关语句执行情况的详细信息,并估计语句对资源的需求 ]

SET  SHOWPLAN_ALL    ON

-- 关闭 [ 返回有关语句执行情况的详细信息,并估计语句对资源的需求 ]

SET  SHOWPLAN_ALL    OFF

---------------------------------------------------------------------------

请记住: SET   STATISTICS  IO    SET  SHOWPLAN_ALL  是互斥的。

 

OK ,现在开始:

首先,我们插入 100 条数据

然后我写了一个查询语句:

SELECT   *   FROM  T_UserInfo  WHERE  USERID = 'ABCDE6EF'

选中以上语句,按 Ctrl + L ,如下图

 

 

这就是 MSSQL 的执行计划:表扫描:扫描表中的行

 

然后我们来看该语句对 IO 的读写:

执行 : SET   STATISTICS  IO  ON

此时再执行该 SQL : SELECT   *   FROM  T_UserInfo  WHERE  USERID = 'ABCDE6EF'

切换到消失栏显示如下:

'T_UserInfo' 。扫描计数 1 ,逻辑读 次,物理读 次,预读 次。

解释下其意思:

四个值分别为:

     执行的扫描次数 ;

     从数据缓存读取的页数 ;

      从磁盘读取的页数 ;

     为进行查询而放入缓存的页数

重要:如果对于一个 SQL 查询有多种写法,那么这四个值中的逻辑读 ( logical reads ) 决定了哪个是最优化的。

 

接下来我们为其建一个聚集索引

执行 CREATE   CLUSTERED   INDEX  INDEX_Userid    ON  T_UserInfo  ( Userid )

然后再执行 SELECT   *   FROM  T_UserInfo  WHERE  USERID = 'ABCDE6EF'

切换到消息栏如下显示:

'T_UserInfo' 。扫描计数 1 ,逻辑读 次,物理读 次,预读 次。

此时逻辑读由原来的 1 变成 2

说明我们又加了一个索引页,现在我们查询时,逻辑读就是要读两页 (1 索引页 +1 数据页 ) ,此时的效率还不如不建索引。

 

此时再选中查询语句,然后再 Ctrl + L ,如下图 :

 

聚集索引查找:扫描聚集索引中特定范围的行

说明,此时用了索引。

 

OK , 到这里你应该已经知道初步知道 MSSQL 查询计划和如何查看对 IO 的读取消耗了吧!

 

 

接下来我们继续:

 

现在我再把测试数据改变成 1000

再执行 SET   STATISTICS  IO  ON , 再执行

SELECT   *   FROM  T_UserInfo  WHERE  USERID = 'ABCDE6EF'

在不加聚集索引的情况下:

'T_UserInfo' 。扫描计数 1 ,逻辑读 次,物理读 次,预读 次。

在加聚集索引的情况下: CREATE   CLUSTERED   INDEX  INDEX_Userid    ON  T_UserInfo  ( Userid )

'T_UserInfo' 。扫描计数 1 ,逻辑读 次,物理读 次,预读 次。

( 其实也就是说此时是读了一个索引页,一个数据页 )

如此,在数据量稍大时,索引的查询优势就显示出来了。

 

 

 

先小总结下

当你构建 SQL 语句时,按 Ctrl + L 就可以看到语句是如何执行,是用索引扫描还是表扫描?

通过 SET   STATISTICS  IO  ON   来查看逻辑读,完成同一功能的不同 SQL 语句,逻辑读

越小查询速度越快 ( 当然不要找那个只有几百条记录的例子来反我 )

   

我们再继续深入:

OK ,现在我们再来看一次,我们换个 SQL 语句,来看下 MSSQL 如何来执行的此 SQL 呢?

现在去掉索引: DROP   INDEX  T_UserInfo . INDEX_Userid

现在打开 [ 显示语句执行情况的详细信息 ] SET  SHOWPLAN_ALL    ON

然后再执行: SELECT   *   FROM  T_UserInfo  WHERE  USERID  LIKE   'ABCDE8%'

看结果栏:结果中有些具体参数,比如 IO 的消耗, CPU 的消耗。

在这里我们只看 StmtText :

SELECT   *   FROM  T_UserInfo  WHERE  USERID  LIKE   'ABCDE8%'

   | --Table Scan(OBJECT:([student].[dbo].[T_UserInfo]), WHERE:(like([T_UserInfo].[Userid], 'ABCDE8%', NULL)))

Ctrl + L 看下此时的图行执行计划:

引用:初探Sql Server 执行计划及Sql查询优化

 

我再加上索引:

先关闭: SET  SHOWPLAN_ALL  OFF

再执行: CREATE   CLUSTERED   INDEX  INDEX_Userid    ON  T_UserInfo  ( Userid )

再开启: SET  SHOWPLAN_ALL  ON

再执行: SELECT   *   FROM  T_UserInfo  WHERE  USERID  LIKE   'ABCDE8%'

查看 StmtText :

SELECT   *   FROM  T_UserInfo  WHERE  USERID  LIKE   'ABCDE8%'

   | --Clustered Index Seek(OBJECT:([student].[dbo].[T_UserInfo].[INDEX_Userid]), SEEK:([T_UserInfo].[Userid] >= 'ABCDE8' AND [T_UserInfo].[Userid] < 'ABCDE9'),    WHERE:(like([T_UserInfo].[Userid], 'ABCDE8%', NULL)) ORDERED FORWARD)Ctrl+L 看下此时的图行执行计划:

Ctrl + L 看下此时的图行执行计划:

引用:初探Sql Server 执行计划及Sql查询优化  

 

在有索引的情况下,我们再写一个 SQL

SET  SHOWPLAN_ALL  ON

SELECT   *   FROM  T_UserInfo  WHERE   LEFT( USERID , 4 )= 'ABCDE8%'

查看 StmtText :

SELECT   *   FROM  T_UserInfo  WHERE   LEFT( USERID , 4 )= 'ABCDE8%'

   | --Clustered Index Scan(OBJECT:([student].[dbo].[T_UserInfo].[INDEX_Userid]), WHERE:(substring([T_UserInfo].[Userid], 1, 4)='ABCDE8%'))

Ctrl+L 看下此时的图行执行计划:

引用:初探Sql Server 执行计划及Sql查询优化  

 

我们再分别看一下三种情况下对 IO 的操作

分别如下:

第一种情况:表 'T_UserInfo' 。扫描计数 1 ,逻辑读 次,物理读 次,预读 次。

第二种情况:表 'T_UserInfo' 。扫描计数 1 ,逻辑读 次,物理读 次,预读 次。

第三种情况:表 'T_UserInfo' 。扫描计数 1 ,逻辑读 次,物理读 次,预读 次。

这说明 :

第一次是表扫描,扫了 7 页,也就是全表扫描

第二次是索引扫描,扫了 1 页索引, 2 页数据页

第三次是索引扫描 + 表扫描,扫了 1 页索引, 7 页数据页

[图形界面也有对CPU和IO的消耗,也可以看出来哪个最优!] 

 

通过比较,嘿嘿,很容易的看出:第二种第三种写法在都有索引的情况下, like 有效的使用索引,而 left 则不能,这样一个最简单的优化的例子就出来了,哈哈。

 

  如果以上你都明白了,那么你可能已经对 SQL 的优化有初步新的想法了,网上一堆堆的 SQL 优化的文章真的是那样吗?你自己试试就知道了,而不必盲目去记那些东西,自己试试,看看 MSSQL 到底是怎么来执行就明白了。

在我举的例子中,用的是聚集索引扫描,字段是字母加数字,大家可以试试看纯数字的、字母的、汉字的等等,了解下 MMSQL 会如何改变 SQL 语句来利用索引。然后再试试非聚集索引是什么情况?用不用索引和什么有关?子查询 MSSQL 是如何执行? IN 用不用索引, LIKE 用不用索引?函数用不用索引? OR AND UNION ?子查询呢?在这里我不一一去试给大家看了,只要知道了如何去看 MSSQL 的执行计划(图形和文本 ) ,很多事情就很明朗了。

 

大总结:

实现同一查询功能的 SQL 写法可能会有多种,如果判断哪种最优化,如果仅仅是从时间上来测,会受很多外界因素的影响,而我们明白了 MSSQL 如何去执行,通过 IO 逻辑读、通过查看图示的查询计划、通过其优化后而执行的 SQL 语句,才是优化 SQL 的真正途径。

 

另外提醒下:数据量的多少有时会影响 MSSQL 对同一种查询写法语句的执行计划,这一点在非聚集索引上特别明显,还有就是在多 CPU 与单 CPU 下,在多用户并发情况下,同一写法的查询语句执行计划会有所不同,这个就需要大家有机会去试验了 ( 我也没有这方面的太多经验与大家分享 )

 

先写这些吧,由于我对 MSSQL 认识还很浅薄,如有不对的地方,还请指正。

引用:初探Sql Server 执行计划及Sql查询优化


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论