我准备开始分析并优化我的查询。在分析之前,我想到了一些问题。

  • MS-SQL Server什么时候使用"Table Scan"?
  • MS-SQL Server什么时候使用"Index Scan"?
  • MS-SQL Server什么时候使用"Index Seek"?
  • MS-SQL Server什么时候使用"Clustered Index Scan"?
  • MS-SQL Server什么时候使用"Clustered Index Seek"?

开始入手

为了解释“显示执行计划”中的“Table Scan”、“Index Scan”、“Index Seek”、“Clustered Index Scan”和“Clustered Index Seek”,先创建新表,并添加一些示例数据进去。下面是创建新表的脚本:

Create Table PerformanceIssue
(
    PRID UniqueIdentifier NOT NULL,
    PRCode Int NOT NULL,
    PRDesc Varchar (100) NOT NULL
)
ON [PRIMARY]

表创建后需要添加一些数据。使用下面的脚本添加100,000条记录进去。脚本执行时间可能比较长,请耐心等待其执行完毕。

Declare @Loop Int
Declare @PRID UniqueIdentifier
Declare @ PRDesc Varchar (100)

Set @Loop = 1
Set @ PRDesc = ''

WHILE @Loop <= 100000
BEGIN
   Set @PRID = NewID()
   Set @PRDesc = ' PerformanceIssue - ' + Convert( Varchar(10),@Loop )
   Insert Into PerformanceIssue Values (@PRID, @Loop, @PRDesc)
   Set @Loop = @Loop + 1
END

脚本成功执行后,数据就添加进去了。

用下面语句来看一下表的内容:

Select PRID, PRCode, PRDesc
From PerformanceIssue
GO

由于记录较长,因此这里就不列出查询结果了。

正如我前面讲到,我想解释何时会有“Table Scan”、“Index Scan”、“Index Seek”、“Clustered Index Scan”和“Clustered Index Seek”。上述哪个会改善性能呢?

当SQL Server返回数据时,我们想知道SQL Server采取何种扫描机制来协助获取数据。首先看一下“Table Scan”。我们想了解什么时候“Table Scan”会产生。

选择“显示执行计划”或者使用热键“Alt + Q”来激活“显示执行计划”,当然也可以用快捷键“Ctrl+K”。

看一下执行下面查询后的“执行计划”结果。

Select PRID, PRCode, PRDesc 
From PerformanceIssue
GO

上面的“执行计划”中,SQL Server用到了“Table Scan”。我问自己为什么会有“Table Scan”,SQL Server是根据什么来使用该方法的。难道是因为我想获取所有100,000条记录吗?于是我换了一个角度进行思考,如果来避免查询中出现“Table Scan”呢?此时我对SQL Server的扫描机制还不是很清楚,那么该如何优化查询呢?下面的SELECT查询中仅选择两列:[PRID, PRCode]。

Select PRID, PRCode
From PerformanceIssue
GO

查询执行后,执行计划和第一个查询一样。于是将查询改变为只检索一个字段 [PRID]。

Select PRID 
From PerformanceIssue

GO

查询执行后,执行计划仍然和第一个查询的相同。对“Estimated row size”属性不需要太大关注。意思我立刻决定只获取一条记录,看看执行计划会如何。查询语句如下:

Select PRID, PRCode, PRDesc 
From PerformanceIssue
Where PRID = 'D386C151-5F74-4C2A-B527-86FEF9712955'
-- PRID GUID value might be differ in your machine

GO

执行完成后,执行计划显示: 

查询仍然使用了“Table Scan”方法来显示数据。

那么,我需要想其它办法来避免“Table Scan”。首先我想到应该给表加上索引。于是我在PRID字段上创建非聚集索引。添加了索引后是否就能避免“Table Scan”?下面我们开始讨论关于“Index Scan”和“Index Seek”的主题。

Index Scan 和 Index Seek

首先在PRID字段上创建非聚集索引。

CREATE UNIQUE NONCLUSTERED INDEX UNC_PRID
ON PerformanceIssue (PRID)
GO

本文假定读者已经知道非聚集索引如何工作的知识。了解非聚集索引更详细的信息,请阅读BOL相关主题,也可参看 http://www.sql-server-performance.com/gv_index_data_structures.asp。下面我们详细讲述“Index Scan”是如何工作的。

执行下面语句并查看执行计划的结果。

Select PRID, PRCode, PRDesc 
From PerformanceIssue
GO

奇怪了,“Table Scan”仍然用到了。为什么SQL Server没有用到那个非聚集索引?于是继续优化查询语句,选择检索两个字段 [PRID, PRCode] 。

Select PRID, PRCode From PerformanceIssue
GO

执行结果是和上一个查询结果一摸一样。于是修改查询为只检索一个字段 [PRID] 。

Select PRID 
From PerformanceIssue
GO

执行计划结果如下: 

“Index Scan”在查询中被用到了,这很好。很自然,接下来的问题就是“Index Scan”什么时候会被用到。字段PRID上有一个索引,查询语句中选中的字段为PRID。执行查询的时候,SQL Server扫描索引页,因此用到了“Index Scan”方法。前面的查询中选择了有索引的和没有索引的字段,SQL Server无法使用“Index Scan”。当查询中只选择有索引的字段时,SQL Server就使用了“Index Scan”。我不清楚SQL Server底层到底是如何判断的,不过通过这些试验,我认为当查询中只选择有索引的字段时,SQL Server就使用“Index Scan”方法。

下面看“Index Seek”方法何时产生。当我看到“Seek”这个词时,第一反应就是条件查询这个主意。

我尝试三种不同的带WHERE语法的查询语句,以找出那种会用“Index Seek”。第一种语句如下:

Select PRID, PRCode, PRDesc 
From PerformanceIssue
Where PRCode = 8
GO

结果显示,执行计划使用了“Table Scan”。 

第二种语句如下:

Select PRID, PRCode, PRDesc
From PerformanceIssue
Where PRDesc = ' PerformanceIssue - 8'
GO

执行计划仍然使用“Table Scan”方法。 

第三种查询语句如下:

Select PRID, PRCode, PRDesc
From PerformanceIssue 
Where PRID = 'D386C151-5F74-4C2A-B527-86FEF9712955'

-- PRID GUID value might be differ in your machine
GO


查询用到了“Index Seek”和“Bookmark Lookup”方法。用到“Index Seek”是因为WHERE后面使用带索引的字段PRID来进行过滤。“Bookmark Lookup”方法被用到是因为查询中选择了没有索引的字段。如果去掉这两个没有索引的字段,那么“Bookmark Lookup”方法就可以去掉。当然如果只返回PRID字段,那么该查询就没什么意义了,因为WHERE语句后面已经给出PRID具体取值了。

我认为“Index Seek”在性能改善上比“Index Scan”和“Table Scan”要好,这主要表现在下面几个方面:

  1. “Index Seek”不需要对表和索引页进行扫描;而“Table Scan”和“Index Scan”需要。
  2. “Index Seek”利用“WHERE”来过滤获取的数据,这样比用“Index Scan”和“Table Scan”快很多。

当我完成这些测试后,我同事问我一个很有意思的问题:SQL Server什么时候使用“Clustered Index Scan”和“Clustered Index Seek”?下面对“Clustered Index Scan”和“Clustered Index Seek”进行实验。

我决定在PRCode上建一个聚集索引来测试“Clustered Index Scan”和“Clustered Index Seek”。

Clustered Index Scan & Clustered Index Seek

下面的脚本删除PRID字段上的索引,并在PRCode字段上创建聚集索引。

Drop Index PerformanceIssue.UNC_PRID
GO
CREATE UNIQUE CLUSTERED INDEX UC_PRCode
ON PerformanceIssue( PRCode)
GO
-------------
Clustered index has been created successfully. 
Index has been created.

关于聚集索引的基础知识请查阅联机帮助的相关主题或者 http://www.sql-server-performance.com/gv_index_data_structures.asp。下面我们将重点放在“Clustered Index Scan”和“Clustered Index Seek”如何被使用上。

执行下面查询语句:

Select PRID, PRCode, PRDesc 
From PerformanceIssue
GO

查询执行后,可以看到执行计划中用到了“Clustered Index Scan”。 

下面用三种不同的WHERE方式来试验何时SQL Server会用到“Clustered Index Seek”。第一种形式如下:

Select PRID, PRCode, PRDesc 
From PerformanceIssue
Where PRDesc = ' PerformanceIssue - 8'
GO

查询执行后,可以看到执行计划中用到了“Clustered Index Scan”。 

第二种形式如下:

Select PRID, PRCode, PRDesc 
From PerformanceIssue
Where PRID = 'D386C151-5F74-4C2A-B527-86FEF9712955'

-- PRID GUID value might be differ in your machine
GO

查询执行后,发现执行计划中用到的仍然是“Clustered Index Scan”。 

第三种形式:

Select PRID, PRCode, PRDesc 
From PerformanceIssue
Where PRCode = 8
GO

这次执行计划用到了“Clustered Index Seek”。 

当在WHERE后用到PRCode字段的时候,“Clustered Index Seek”被用到。执行计划对聚集索引表检索的时候,因为在选取的字段中,包括没有索引的字段,所以不用用到“Bookmark Lookup”方法。

我个人认为,从改善性能角度考虑,“Clustered Index Seek”比“Clustered Index Scan”和“Index Seek”要好。

  1. “Clustered Index Seek”不需要扫描整个聚集索引页。
  2. 和“Index Scan”相比,对于检索选择的字段包含那些没有索引的字段时,“Clustered Index Seek”不会有“Bookmark Lookup”方法出现。

通过这些试验,我对执行计划的应用积累了实际经验。我知道哪种扫描机制可以提高性能,从而是的客户满意

  注意,如果表没有聚集索引,建立了非聚集索引,那么非聚集索引使用的是行号,如果此时你又添加了聚集索引,那么所有的非聚集索引引用的RID都要改为聚集索引键。这对性能的消耗是非常大的,因此最好先建立聚集索引,在建立非聚集索引。

8、聚集表上的非聚集索引

  使用聚集表上的非集群索引时,还有一些类似性-但同样也有区别。和堆上的非集群索引一样,索引的非叶级及诶单的工作与使用聚集索引时几乎一样。区别出现在叶级。

  在叶级,与使用其他两种索引结构所看到的内容有相当明显的区别。聚集表上的非集群索引有另外一个索引来查找。使用聚集索引,当到达叶级时,可以找到实际的数据,当使用堆上的非集群索引,不能找到实际的数据,但是可以找到能够直接获得数据的标识符(仅仅多了一步)。使用聚集表上的非聚集索引,可以找到聚集键。也就是说,找到足够的信息继续并利用聚集索引。

  以上理解,说白了就是,当使用非聚集索引时,就是遍历非聚集索引找到聚集索引,最后多次采用聚集索引找到数据。

执行计划sql的更多相关文章

  1. Mysql学习---视图/触发器/存储过程/函数/执行计划/sql优化 180101

    视图 视图: 视图是一个虚拟表(非真实存在),动态获取数据,仅仅能做查询操作 本质:[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,并可以将其当作表来使用.由 ...

  2. 固定执行计划-SQL PROFILE手工绑定

    固定(稳定)执行计划 你的应用的功能时快时慢,变化比较大,功能的性能能够保持一种稳定的状态,ORACLE 固定执行计划,采用以下这几种方式 oracle 9i使用 Outline oracle 10g ...

  3. SQL Server 执行计划缓存

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/内存池/缓冲区 概述 了解执行计划对数据库性能分析很重要,其中涉及到了语句性能分析与存储,这也是写这篇文章的目的,在了解执行计划之 ...

  4. 程序员眼中的 SQL Server-执行计划教会我如何创建索引?

    先说点废话 以前有 DBA 在身边的时候,从来不曾考虑过数据库性能的问题,但是,当一个应用程序从头到脚都由自己完成,而且数据库面对的是接近百万的数据,看着一个页面加载速度像乌龟一样,自己心里真是有种挫 ...

  5. 如何使用SQLPLUS分析SQL语句(查询执行计划跟踪)

    方法一:autotrace 1,  connect sys/密码 as sysdba,在sys用户下运行$ORACLE_HOME/sqlplus/admin/plustrce.sql这段sql的实际内 ...

  6. sql server 执行计划(execution plan)介绍

    大纲:目的介绍sql server 中执行计划的大致使用,当遇到查询性能瓶颈时,可以发挥用处,而且带有比较详细的学习文档和计划,阅读者可以按照我计划进行,从而达到对执行计划一个比较系统的学习. 什么是 ...

  7. Atitit sql执行计划

    Atitit sql执行计划 1.1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的 Oracle中的执行计划 ...

  8. SQL 执行计划(一)

    缓存执行计划  SQL Server 2008提供了一些服务器对象来分析执行计划Sys.dm_exec_cached_plans:    包含缓存的执行计划,每个执行计划对应一行.Sys.dm_exe ...

  9. SQL Sever 2008性能分析之执行计划

    一直想找一些关于SQL语句性能调试的权威参考,但是有参考未必就能够做好调试 2的工作.我深信实践中得到的经验是最珍贵的,书本知识只是一个引导.本篇来源于<Inside Microsoft SQL ...

随机推荐

  1. 如何用 windows+github搭建一个优美的hexo博客

    1.Hexo简单介绍 Hexo 是一个快速.简洁且高效的博客框架.Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页. 风一般的速度Hexo基于Nod ...

  2. 1e6等于多少?

    如果抽象成这样:aeb 要求a不能不写,也就是说是1也要写上 b必须是整数. 实现上就是 a*10^b a乘以10的b次方 所以楼主的就是1*10^6 100000

  3. Android ViewPager+TabHost实现首页导航

    今天发的是TabHost结合ViewPager实现首页底部导航的效果,虽然说网上有很多这样的Demo,不过呢,我还是要把自己练习写的发出来,没错!就是这么任性: 先上效果图,如下: 代码里面有注释,就 ...

  4. Android 隐式 Intent 跳转注意事项

    前几天正好看到<阿里巴巴 Android 开发手册>中提到的: “Activity 间通过隐式 Intent 的跳转,在发出 Intent 之前必须通过 resolveActivity 检 ...

  5. jdbc、Connection pool、jndi的理解和关系

    一.概念和理解: ①.jdbc:Java Data Base Connectivity,java数据库连接,最为传统的一种方式,直接连接操作数据库,需要连接时创建连接,使用结束时销毁连接. ②.Con ...

  6. 缓存溢出Buffer Overflow

    缓存溢出(Buffer overflow),是指在存在缓存溢出安全漏洞的计算机中,攻击者可以用超出常规长度的字符数来填满一个域,通常是内存区地址.在某些情况下,这些过量的字符能够作为“可执行”代码来运 ...

  7. MonkeyRunner之MonkeyRecorder录制回放脚本

    MonkeyRunner强大的功能之一便是允许用户自由录制需要的脚本,录制和回放需要两个脚本文件 monkey_recorder.py和monkey_playback.py 首先来看 monkey_r ...

  8. redis 笔记(二)

    在上一篇中提到了数据类型 ,在本章中就具体说说这几种数据类型: sting :set /get / del / append /strlen 简单的对key-->value 写入读取删除增减 i ...

  9. 如何在PB中调用 Microsoft WEB 浏览器 控件?

    PB中使用Microsoft Web Browser控件步骤: 在pb的某窗口中加入OLE对象,选择Insert control(插入控件),然后选中"Microsoft WEB 浏览器&q ...

  10. IOS 应用管理(九宫格) 总结笔记

    1. 开发前的思路 ========================================1> 从mainBundle中加载Plist2> 按照plist中的数据数量先确定各个a ...