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

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

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

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

--建测试表

CREATETABLET_UserInfo

(

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

RegTimedatetime,Tel varchar(20),

)

--插入测试数据

DECLARE@IINT

DECLARE@ENDIDINT

SELECT@I=1

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

WHILE@I<=@ENDID

BEGIN

INSERTINTOT_UserInfo

SELECT'ABCDE'+CAST(@IASVARCHAR(20))+'EF',''+CAST(@IASVARCHAR(20)),

GETDATE(),'876543'+CAST(@IASVARCHAR(20))

SELECT@I=@I+1

END

--相关SQL语句解释

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

--建聚集索引

CREATECLUSTEREDINDEXINDEX_UseridONT_UserInfo(Userid)

--建非聚集索引

CREATENONCLUSTEREDINDEXINDEX_UseridONT_UserInfo(Userid)

--删除索引

DROPINDEXT_UserInfo.INDEX_Userid

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

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

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

SETSTATISTICSIOON

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

SETSTATISTICSIOOFF

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

SETSHOWPLAN_ALLON

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

SETSHOWPLAN_ALLOFF

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

请记住:SETSTATISTICSIOSETSHOWPLAN_ALL是互斥的。

OK,现在开始:

首先,我们插入100条数据

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

SELECT*FROMT_UserInfoWHEREUSERID='ABCDE6EF'

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

<?XML:NAMESPACE PREFIX = V />

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

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

执行:SETSTATISTICSIOON

此时再执行该SQL:SELECT*FROMT_UserInfoWHEREUSERID='ABCDE6EF'

切换到消失栏显示如下:

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

解释下其意思:

四个值分别为:

执行的扫描次数;

从数据缓存读取的页数;

从磁盘读取的页数;

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

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

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

执行CREATECLUSTEREDINDEXINDEX_UseridONT_UserInfo(Userid)

然后再执行SELECT*FROMT_UserInfoWHEREUSERID='ABCDE6EF'

切换到消息栏如下显示:

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

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

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

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

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

说明,此时用了索引。

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

接下来我们继续:

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

再执行SETSTATISTICSIOON,再执行

SELECT*FROMT_UserInfoWHEREUSERID='ABCDE6EF'

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

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

在加聚集索引的情况下:CREATECLUSTEREDINDEXINDEX_UseridONT_UserInfo(Userid)

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

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

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

先小总结下

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

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

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

我们再继续深入:

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

现在去掉索引:DROPINDEXT_UserInfo.INDEX_Userid

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

然后再执行:SELECT*FROMT_UserInfoWHEREUSERIDLIKE'ABCDE8%'

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

在这里我们只看StmtText:

SELECT*FROMT_UserInfoWHEREUSERIDLIKE'ABCDE8%'

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

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

我再加上索引:

先关闭:SETSHOWPLAN_ALLOFF

再执行:CREATECLUSTEREDINDEXINDEX_UseridONT_UserInfo(Userid)

再开启:SETSHOWPLAN_ALLON

再执行:SELECT*FROMT_UserInfoWHEREUSERIDLIKE'ABCDE8%'

查看StmtText:

SELECT*FROMT_UserInfoWHEREUSERIDLIKE'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

SETSHOWPLAN_ALLON

SELECT*FROMT_UserInfoWHERELEFT(USERID,4)='ABCDE8%'

查看StmtText:

SELECT*FROMT_UserInfoWHERELEFT(USERID,4)='ABCDE8%'

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

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

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

分别如下:

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

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

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

这说明:

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

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

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

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

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

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

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

大总结:

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

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

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

更多相关文章

  1. Android使用service后台更新计划任务
  2. Android应用程序与SurfaceFlinger服务的关系概述和学习计划
  3. Android应用程序与SurfaceFlinger服务的关系概述和学习计划
  4. SEAndroid安全机制简要介绍和学习计划
  5. Android应用程序与SurfaceFlinger服务的关系概述和学习计划
  6. Android应用程序与SurfaceFlinger服务的关系概述和学习计划
  7. Binder研究之一   制定计划
  8. Android应用程序与SurfaceFlinger服务的关系概述和学习计划
  9. SEAndroid安全机制简要介绍和学习计划

随机推荐

  1. 电商网站项目总结(面向对象编程篇)
  2. ThinkPHP常用小知识
  3. php无wsdl webservice服务用法
  4. curl获取网页内容出现乱码或为空的解决方
  5. PHP: Join two separate mysql queries i
  6. 从PHP智能模板中剥离空白
  7. 如何在“”之前删除多个UTF-8 BOM序列?
  8. php吧字符串直接转换成数组处理
  9. 为什么我对JSON对象的AJAX调用会返回其特
  10. PHP print_r 转换/还原为数组