执行计划教会我如何创建索引,查询性能优化

其实这是一篇没有技术含量的文章,精通SQL优化的请绕道。这个缘起于在优化一个SQL过程中,同事问了我一个问题,为什么SQL中存在隐式转换,但是执行计划没有变? 我思索了一下,觉得这个问题也有点意思,说不定有些对隐式转换了解得不深入的同学都有此疑问,那么下面结合上下文场景做一个细节方面的解答。

先说点废话

以前有 DBA 在身边的时候,从来不曾考虑过数据库性能的问题,但是,当一个应用程序从头到脚都由自己完成,而且数据库面对的是接近百万的数据,看着一个页面加载速度像乌龟一样,自己心里真是有种挫败感。代码的优化问题,这是属于程序员的职责范围之内,对于我来说,这一方面比较好探查些,因为都是自己熟悉的,用 EF 或 SQL Server Profiler 跟踪一下程序代码产生的 SQL,如果有问题,直接优化程序代码就可以了,如果 SQL 没问题,那就得优化数据库了,对于我来说,这是一个无人区。

前两天,自己瞎搞了一个测试:程序员眼中的 SQL Server-非聚集索引能给我们带来什么?,因为对索引不是很熟悉,所以测试得到结果没有任何价值,甚至有些误导人,这边说声抱歉,在哪跌倒在哪爬起来。

这一篇文章修修改改,已经写了很久了,还是感觉好像自己没讲清楚,鉴于本人水平,就先这样写吧,待本人水平提高之后,再进行修补。

我们一个系统中使用了ORMLite框架,粗心的开发人员弄出了不少下面这样的SQL语句,都存在隐式转换问题,如下所示,表machine_stop_alarm_msg 的结构如下,字段machine_no、status都为VARCHAR(10),但是下面SQL,传入的变量@P0,@P1都是NVARCHAR(4000)类型。

应用场景

还是用商品表(Product)作为示例,表结构如下:

图片 1

存在这样一种业务场景:获取某个供应商(ProviderID),状态为已售(State 为 1)的商品列表,排序方式为生产日期(ProduceTime)降序,有可能我们应用程序在显示数据的时候用到分页,这边我们查询前 100 行。翻译为 SQL 代码:

SELECT TOP 100 
[ID],
[Name],
[Remarks],
[ProviderID],
[ProduceTime],
[State]
FROM [TestDB].[dbo].[Product]
WHERE [ProviderID]=1 AND [State]=1
ORDER BY [ProduceTime] DESC

上面这个业务场景,在我们一般的应用程序中基本上都会遇到,有时候数据量不是很大的时候,我们一般不会做任何数据库优化,但是你看了下面的实践,你是否应该考虑下,为你现在的数据库加个索引呢?

在写作的过程也学习到了,SQL查询优化程序也并不一定会使用查询参数中字段的相关索引,而是根据查询数据量的多少而产生的查询成本,来决定是使用查询参数中的字段索引,还是使用聚集索引或全表扫描。

 

SQL Server 执行计划

图片 2

SQL Server 执行计划,是我们分析 SQL 执行情况的一大利器,通过它,我们也可以很方面的查看索引的执行,在实践之前,需要了解一些必备技能,以下知识点摘自-看懂 SqlServer 查询计划。

SQL Server 有二种索引:聚集索引和非聚集索引。二者的差别在于:【聚集索引】直接决定了记录的存放位置, 或者说:根据聚集索引可以直接获取到记录。【非聚集索引】保存了二个信息:1.相应索引字段的值,2.记录对应聚集索引的位置(如果表没有聚集索引则保存记录指针)。 因此,如果能通过【聚集索引】来查找记录,显然也是最快的。

SQL Server 会有以下方法来查找您需要的数据记录:

  1. 【Table Scan】:遍历整个表,查找所有匹配的记录行。这个操作将会一行一行的检查,当然,效率也是最差的。
  2. 【Index Scan】:根据索引,从表中过滤出来一部分记录,再查找所有匹配的记录行,显然比第一种方式的查找范围要小,因此比【Table Scan】要快。
  3. 【Index Seek】:根据索引,定位(获取)记录的存放位置,然后取得记录,因此,比起前二种方式会更快。
  4. 【Clustered Index Scan】:和【Table Scan】一样。注意:不要以为这里有个Index,就认为不一样了。 其实它的意思是说:按聚集索引来逐行扫描每一行记录,因为记录就是按聚集索引来顺序存放的。 而【Table Scan】只是说:要扫描的表没有聚集索引而已,因此这二个操作本质上也是一样的。
  5. 【Clustered Index Seek】:直接根据聚集索引获取记录,最快!

所以,当发现某个查询比较慢时,可以首先检查哪些操作的成本比较高,再看看那些操作在查找记录时, 是不是【Table Scan】或者【Clustered Index Scan】,如果确实和这二种操作类型有关,则要考虑增加索引来解决了。 不过,增加索引后,也会影响数据表的修改动作,因为修改数据表时,要更新相应字段的索引。所以索引过多,也会影响性能。 还有一种情况是不适合增加索引的:某个字段用0或1表示的状态。例如可能有绝大多数是1,那么此时加索引根本就没有意义。 这时只能考虑为0或者1这二种情况分开来保存了,分表或者分区都是不错的选择。

中心思想就是关于SQL语句的“查询参数”(SARG)与索引的使用。符合SARG格式的数据肯定会使用到相应的索引呢?先给出答案,不是。

图片 3

应用分析

我们先不建任何索引(除了主键 ID 的聚集索引),来看一下上面 SQL 代码,在 SQL Server 执行计划中的执行情况:

图片 4

可以看到,查询开销基本上被 SORT 霸占了,看到这种情况,按照正常的思维,我们首先考虑的是为 ProduceTime 创建一个非聚集索引,然后按照 DESC 排序,但有时候我们要沉下心思考一下,是不是用 ID 排序会更好呢?因为在 Product 表中,ID 为自增字段,ProduceTime 在添加的时候获取的是当前时间,在 SQL 排序中,其实 ID 和 ProduceTime 的排序效果是一样的,但是执行性能方面确实天壤之别,我们看一下执行计划就知道了:

图片 5

从上面的执行计划中,我们可以很直观的看出差别,所以在写 SQL 的时候,一定要慎重啊,这边为了方便展示,我们还是以 ProduceTime 字段进行排序,按照 ID 排序,虽然没有了 SORT 性能开销,但是发现查询记录为“Clustered Index Scan”,这是全表查询的意思,我们理想的应该是“Index Seek”或者“Clustered Index Seek”,因为这种是按照索引查询,速度最快。按照我们程序员的理解,应该创建一个非聚集索引,比如下面 IX_Product_Provider_State 索引:

图片 6

创建好之后,我们再来执行一下 SQL 代码:

图片 7

“Key Lookup(Clustered)”记录,其实还是全表进行查找,默认通过聚集索引(PK_Product),我们可能会有疑问,索引就是按照查询及排序方式创建的啊,为什么还是这种情况?这时候我们看一下 SELECT 后面的字段就知道了,我们查询显示的是 Product 表中所有字段,但是 IX_Product_Provider_State 非聚集索引,只是针对的查询条件字段,并没有吧查询显示字段包含进来,在创建索引窗口中,“索引键 列” TAB 的旁边有个“包含性 列”,我们把其他显示字段加进来,看下执行效果:

图片 8

“Index Seek”,这就是我们想要的效果,其实关于索引的创建有很多的现实问题,比如组合字段索引和单个字段索引有何不同?就像上面示例中的查询用例,如果 ProduceTime 排序在其他查询条件中也存在,是不是应该拉出来创建一个索引?还是像上面一样,和查询条件一起创建一个组合字段索引?还有一种情况就是,在一个应用程序查询中,存在单个字段的查询,也存在组合字段的查询,那这时候我们是创建单个字段索引?还是创建组合字段索引呢?这几个问题,你创建一下索引,然后用“ SQL 执行计划”试试就知道了。

例如:Select * from WBK_PDE_LIST_ORG where cop_g_no='11000' ,假设在cop_g_no上建立了非聚集索引,那么当查询语句得出的结果数量小于某个数量阀值时,例如查询结果的数量小于600条时,会使用到非聚集索引,但当查询结果数量大于600条时,却可能不会使用非聚集索引,可能会使用聚集索引或全表扫描。

DECLARE  @P0 nvarchar(4000),@P1 nvarchar(4000);

 

SET @P0='1';

SET @P1='K172';

 

SELECT [recid],[machine_no] 

   ,[stop_stime] 

   ,[stop_etime] 

   ,[status] 

   ,[memo] 

   ,[createddate]  

FROM machine_stop_alarm_msg t  

WHERE 1=1  

AND t.status=@P0  

AND t.machine_no in(@P1 )  

ORDER BY machine_no, 

   stop_stime ;  

总结

针对上面的查询用例,我个人觉得,最好的方案是:排序字段使用 ID,按照实际应用场景,提取出需要查询的字段,避免 SELECT *,这样会减少在添加“包含性 列”的字段,创建 IX_Product_Provider_State 非聚集索引,索引字段为:ProviderID 和 State,如果 State 的值不是多变的(比如值为 1 和 0),尽量不要创建 State 字段的非聚集索引。

做完这些,你会发现,你的应用程序像飞的一样。

摘自:

 

 

在编写SQL语句的WHERE 子句时,你是否考虑过WHERE子句中的条件参数的编写格式要符合“ (查询参数:SARG )”规则,SQL SERVER的查询优化程序才能建立有效的利用索引的计划。

 

在进行具体分析之前,首先建立以下索引。当然索引2、3与索引4、5的名称需要自己修改。

machine_stop_alarm_msg 表只有一个聚集索引PK_machine_stop_alarm_msg,字段为recid。

序号

索引类型

 SQL语句

1

主键聚集索引

ALTER TABLE [dbo].[WBK_PDE_LIST_ORG_HISTROY] ADD  CONSTRAINT [PK_WBK_PDE_LIST_ORG_HISTROY] 

PRIMARY KEY CLUSTERED(

[WBOOK_NO] ASC,

[G_NO] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF

, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON

, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

2

非聚集索引(无INCLUDE)

 CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_QTY1] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[QTY_1] ASC

) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF

, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON

, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

3

 

CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_COP_G_NO] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[COP_G_NO] ASC

) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF,

 IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 

ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

4

非聚集索引(有INCLUDE)

 CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_QTY1] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[QTY_1] ASC

)

INCLUDE ( [WBOOK_NO],[G_NO],[CODE_T],[COP_G_NO],[UNIT_1],[TRADE_TOTAL],[GROSS_WT])

 WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF

, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 

ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

5

 

CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_COP_G_NO] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[COP_G_NO] ASC

)

INCLUDE ( [WBOOK_NO],[G_NO],[CODE_T],[QTY_1],[UNIT_1],[TRADE_TOTAL],[GROSS_WT])

 WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF,

 IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 

ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

     

图片 9

 

 

 

当时我优化的时候,就觉得这个SQL语句存在两个问题:1 缺少索引; 2 存在隐式转换问题。当时创建了下面索引,并要求开发人员修改SQL,避免隐式转换。

图片 10

CREATE NONCLUSTERED INDEX ix_machine_stop_alarm_msg_n1

ON [dbo].[machine_stop_alarm_msg] ([machine_no],[status])

INCLUDE ([recid],[stop_stime],[stop_etime],[memo],[createddate])

GO

 

 

Index Seek 运算符利用索引的查找功能从非聚集索引中检索行。

 

Index Scan 运算符从 Argument 列中指定的非聚集索引中检索所有行。如果可选的 WHERE:() 谓词出现在 Argument 列中,则只返回满足该谓词的那些行。

在测试环境测试时,我们先不增加这个索引,就出现了下面一个场景,两者都是走聚集索引扫描:

Clustered Index Scan 运算符会扫描查询执行计划的 Argument 列中指定的聚集索引。如果出现可选 WHERE:()谓词,则只返回满足该谓词的行。

 

Clustered Index Seek 运算符可以利用索引的查找功能从聚集索引中检索行。Argument 列包含所使用的聚集索引名称和 SEEK:() 谓词。存储引擎仅使用索引来处理满足此 SEEK:() 谓词的行。它还包括 WHERE:() 谓词,其中存储引擎对满足 SEEK:() 谓词的所有行进行计算,但此操作是可选的,并且不使用索引来完成此过程。

1: 执行计划走聚集索引扫描(Cluster Index Scan)

 

SET STATISTICS IO ON;

SET STATISTICS TIME ON;

DECLARE  @P0 nvarchar(4000),@P1 nvarchar(4000);

 

SET @P0='1';

SET @P1='K172';

SELECT [recid],[machine_no] 

   ,[stop_stime] 

   ,[stop_etime] 

   ,[status] 

   ,[memo] 

   ,[createddate]  

FROM machine_stop_alarm_msg t  

WHERE 1=1  

AND t.status=@P0  

AND t.machine_no in(@P1 )  

ORDER BY machine_no, 

   stop_stime ;  

 

SET STATISTICS IO OFF;

SET STATISTICS TIME OFF;

Table Scan 运算符从查询执行计划的 Argument 列所指定的表中检索所有行。如果 WHERE:()谓词出现在 Argument 列中,则仅返回满足此谓词的那些行。

图片 11

Filter 运算符扫描输入,仅返回那些符合 Argument 列中的筛选表达式(谓词)的行。

图片 12

Inner Join 逻辑运算符返回满足第一个(顶端)输入与第二个(底端)输入所组成的联接的每一行。

 

SQL Server 2005 Service Pack 2 中引入的 Key Lookup 运算符是在具有聚集索引的表上进行的书签查找。Argument 列包含聚集索引的名称和用来在聚集索引中查找行的聚集键。

2: 执行计划走聚集索引扫描(Cluster Index Scan)

 

 

RID Lookup 是在使用提供的行标识符 (RID) 在堆上进行的书签查找。Argument 列包含用于查找行的书签标签和从中查找行的表的名称。RID

SET STATISTICS IO ON;

SET STATISTICS TIME ON;

DECLARE  @P0 VARCHAR(10),@P1 VARCHAR(10);

 

SET @P0='1';

SET @P1='K172';

SELECT [recid],[machine_no] 

   ,[stop_stime] 

   ,[stop_etime] 

   ,[status] 

   ,[memo] 

   ,[createddate]  

FROM machine_stop_alarm_msg t  

WHERE 1=1  

AND t.status=@P0  

AND t.machine_no in(@P1 )  

ORDER BY machine_no, 

   stop_stime ;  

 

SET STATISTICS IO OFF;

SET STATISTICS TIME OFF;

1. 有效地查询参数

图片 13

得到相同查询结果的SQL语句的写法有很多种,那么应该如何决定采用哪种SQL语句编写方式比较有用呢?最重要的考虑因素之一是WHERE 条件子句, WHERE子句限制了查询所要返问的记录数量,查询优化程序会尝试判断己有的索引,分析对查找符合WHERE子句条件的记录是否有帮助。

 

查询优化程序首先就要查看WHERE 子句中所有的条件,以决定这些条件在限制SQL SERVER 访问数据时是否有用。换句话说,查询子句是否有用要看查询参数(Searchable Arguments , SARG〕 

这里两者的执行计划一样,这个应该很好理解,缺少相关索引,而且发生隐式转换的不是索引所在的字段,那么即使存在隐式转换,它的执行计划是一样的。 这里没有太多要解释的。

很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如: 
SELECT *  FROM [WBK_PDE_LIST_ORG_HISTROY] where QTY_1>53 and COP_G_NO='90206884'

那么我们接下来看看看增加了索引后,两者的实际执行计划。

  和执行: 
SELECT *

 

  FROM [WBK_PDE_LIST_ORG_HISTROY] where  COP_G_NO='90206884' and QTY_1>53 
一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果QTY_1是一个非聚集索引,那么前一句仅仅从QTY_1大于53的记录中查找就行了;而后一句则要先从全表中查找看有几个COP_G_NO='90206884'的,而后再根据限制条件条件QTY_1>53来提出查询结果。 

图片 14

事实上,这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以根据WHERE子句中的搜索条件进行自动优化,建立有效的索引使用计划。 

图片 15

上面两句的IO情况是一样的,都是250次逻辑读取操作。具体执行结果如下:

 

(61 行受影响)

现在同事纠结的就是即使发生了隐式转换,为什么执行计划还是走索引查找(Index Seek)呢? 其实很多人有一个误区,SQL Server当中并不是所有的隐式转换都会导致索引扫描(Index Scan),关于这个请见我这篇博客SQL SERVER中什么情况会导致索引查找变成索引扫描 。也就是说隐式转导致索引扫描也是有条件的。这里不再做展开讲,没有太多意思。另外,我们再来对比一下两者的执行计划。

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取250 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

 

 

上面发生隐式转换的SQL的执行计划,多了一个常量扫描(Constant Scan),常量扫描做的工作是根据用户输入的SQL中的常量生成一个行 ,MSDN的介绍如下:

(61 行受影响)

"The Constant Scan operator introduces one or more constant rows into a query. A Compute Scalar operator is often used after a Constant

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取250 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

Scan to add columns to a row produced by the Constant Scan operator"

从Managemenet studio中可以看出上面两句的查询执行计划都是一样的。如下图。

 

 

常量扫描会引入一个或者多个常量行到一个查询中;通常情况下紧跟常量扫描的是计算标量运算符,计算标量运算符会为常量扫描运算符产生的行添加列。

图片 16

图片 17

所以上面两句的执行效率是一样的。

如果你想知道执行计划里面的Expr1004、 Expr1005、Expr1003对应啥,看看执行计划就知道了(其中Expr1003为(62),一开始不明其什么意义,后面咨询了宋大神,才知道62是个flag,意思是等于号)

 

图片 18

虽然查询优化器可以根据WHERE子句自动的进行查询优化,但大家仍然有必要了解一下“查询优化器”的工作原理,我们有时会以查询参数这个名词来泛指在WHERE 子句中所有的条件,但此处使用SARG缩写来代表查询参数的有效格式。在大多数状况下,查询优化程序只能对符合SARG 条件的WHERE子句通过索引找到优化的执行方式。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。 

 

 

发生隐式转换的SQL还多了一个Nested Loop(Inner Join)操作。另外,即使这两个SQL依然都是索引查找(Index Seek),但是两种的IO开销还是有所区别的。

SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值得范围内的匹配或者两个以上条件的AND连接。SARG 包含常量描述式(或是可以解析成常量的变量)来与数据表中的字段做比较。SARG 的格式是:

图片 19

列名 操作符 <常数 或 变量> 

 

图片 20

<常数 或 变量> 操作符 列名 

 

列名出现在操作符的一边,而常量或变量出现在另一边。如果列名同时出现在操作的两边就不算是SARG。

 

SARG包含以下操作符=、>、<、>=、<=、BETWEEN及部分情况下的LIKE。LIKE是否符合SARG,要看通配符%所在的位置。例如:LIKE '胡%'就是符合SARG,但是'%胡'就不符合SARG。因为以通配符开头无法限制SQL SERVER查询记录的数量,索引的摆放依然是以小到大,或以大到小顺序排列,如果以通配符“%”开头就无法利用有序的结构,以二分法来快速查找数据。

图片 21

简言之,在查询子句中,SARG代表用来查找的常量或变量可以直接与索引键值进行比较,下面是一些常用SARG与执行索引的关系。

 

 

图片 22

 序号 索引
 SQL语句与查询执行计划  记录数  执行成本

1

索引4

SELECT [WBOOK_NO]      ,[COP_G_NO] ,[G_NO],[CODE_T]             

      ,[QTY_1],[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT] FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1=1

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取29 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

29

0.0267688

2

索引1

SELECT * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1=1

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取1314 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

1314

1.03687

     

   

3

索引4

SELECT  [WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T],[QTY_1]

      ,[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT] FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1>=312

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取29 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

29

0.0268468

4

索引1

SELECT * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1>=312

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取1314 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

1314

1.03687

5

索引4

SELECT  [WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T] 

      ,[QTY_1],[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT] FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1<2

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取29 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

29

0.026875

6

索引1

SELECT * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1<2

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取1314 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

1314

1.03687

7

索引4

SELECT  [WBOOK_NO],[COP_G_NO] ,[G_NO],[CODE_T]             

      ,[QTY_1],[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT]  FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1!>1

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取29 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

29

0.026875

8

索引1

SELECT  * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1!>1

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取1314 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

1314

1.03687

9

索引2

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where QTY_1 between 412 and 500

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取1021 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

1021

0.959746

10

索引3

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where cop_g_no like '80215%'

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取320 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

   
     

320

0.316824

11

索引3

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where cop_g_no like '802%'

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where cop_g_no like '!%'

   
   

表'WBK_PDE_LIST_ORG_HISTROY'。扫描计数1,逻辑读取1314 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。

1314

1.03687

     

   

 

SQL SERVER查询分析优化器对于每一条查询语句的WHERE子句进行评估,看是使用索引的查询成本还是使用聚集索引扫描的查询成本低。

从上表中我们可以看出根据不同的查询语句与不同的查询字段,会使用不同的索引,如果当查询出来的记录数比较多时,也就是超过了直接使用聚集索引扫描或全表扫描查询出来的数据时,即使WHERE子名是SARG格式的写法,他也将使用放弃使用相应的索引,而使用全表扫描与聚集索引扫描(例如上表中的2,4,6,8,11)。

 

使用索引

查询语句

查询记录数量

执行成本 

索引2

9

1021

0.959746

索引3

10

320

0.316824

索引4

1,3,5,7

29

0.026875

索引5

     

索引1

2,4,6,8,11

1314

1.03687

本文由星彩网app下载发布于星彩彩票app下载,转载请注明出处:执行计划教会我如何创建索引,查询性能优化

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。