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

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

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 ;  

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

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

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

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

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;

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

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;

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

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

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

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

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

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

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

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

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

SQL SERVER中隐式转换的一些细节浅析的更多相关文章

  1. SQL Server 隐式转换引发的躺枪死锁-程序员需知

    在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍 ...

  2. SQL Server 隐式转换引发的死锁

    在SQL Server的应用开发过程(尤其是二次开发)中可能由于开发人员对表的结构不够了解,造成开发过程中使用了不合理的方式造成数据库引擎未按预定执行,以致影响业务.这是非常值得注意的.这次为大家介绍 ...

  3. SQL Server中行列转换 Pivot UnPivot

    SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PI ...

  4. SQL Server全时区转换

    SQL Server全时区转换 假如你的应用程序是跨国(例如跨国银行交易)使用的话,那么数据库的一些国际化特性支持可以说是非常重要 其中最常见的就是各国时区上的差异,由于SQL Server getd ...

  5. piap.excel 微软 时间戳转换mssql sql server文件时间戳转换unix 导入mysql

    piap.excel 微软 时间戳转换mssql sql server文件时间戳转换unix 导入mysql 需要不个mssql的sql文件导入mysql.他们的时间戳格式不同..ms用的是自定义的时 ...

  6. Scala 深入浅出实战经典 第65讲:Scala中隐式转换内幕揭秘、最佳实践及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  7. sql server动态行列转换

    原文链接:https://www.cnblogs.com/gaizai/p/3753296.html sql server动态行列转换 一.本文所涉及的内容(Contents) 本文所涉及的内容(Co ...

  8. SQL Server中datetimeset转换datetime类型问题浅析

    在SQL Server中,数据类型datetimeoffset转换为datetime类型或datetime2类型时需要特别注意,有可能一不小心你可能会碰到下面这种情况.下面我们构造一个简单案例,模拟一 ...

  9. SQL Server 进制转换函数

    一.背景 前段时间群里的朋友问了一个问题:“在查询时增加一个递增序列,如:0x00000001,即每一个都是36进位(0—9,A--Z),0x0000000Z后面将是0x00000010,生成一个像下 ...

随机推荐

  1. Cesium原理篇:3最长的一帧之地形(1)

    前面我们从宏观上分析了Cesium的整体调度以及网格方面的内容,通过前两篇,读者应该可以比较清楚的明白一个Tile是怎么来的吧(如果还不明白全是我的错).接下来,在前两篇的基础上,我们着重讨论一下地形 ...

  2. 微信小程序小技巧系列《二》show内容展示,上传文件编码问题

    作者:gou-tian 来自:github show内容展示 尝试用微信小程序的template组件实现.同时,尝试页面间转跳时传参,在目标页面引入模板文件实现 写的更少,做的更多 篇幅有限详细代码此 ...

  3. 数据库基础及T-SQL语句

    字符类型: int 整型float 小数double 小数varchar(20) 字符串bit 布尔型数据datetime 日期时间类型text 长文本 (以下两种不经常使用) money 存货币im ...

  4. 四大组件之ContentProvider

    前言 ContentProvider作为Android的四大组件之一,是属于需要掌握的基础知识,可能在我们的应用中,对于Activity和Service这两个组件用的很常见,了解的也很多,但是对Con ...

  5. Redis数据类型,以及应用场合

    Redis常用的数据类型为String,Hash,List,Set等,简介如下: String 1.String 常用命令: 除了get.set.incr.decr mget等操作外,Redis还提供 ...

  6. How PhoneGap & Titanium Works

    转载自 http://www.appcelerator.com/blog/2012/05/comparing-titanium-and-phonegap/ How PhoneGap Works As ...

  7. Openfire 4.1.0

    http://www.igniterealtime.org/downloads/index.jsp 服务器端口 接口 端口   类型 描述 所有的地址 5222 客户端到服务器 客户端使用标准端口连接 ...

  8. ActiveX(四) mshtml 命名空间 重要接口简介

    在上一篇随笔 ActiveX(三)ActiveX 调用 Js 中,我们已经可以获得js中window对象的强类型接口.即 mshtml.IHTMLWindow2 ,通过该接口.我们可以调用js函数.那 ...

  9. thinkphp怎么设置输入网址直接进入首页

    1.设置apache服务器的时候,文件的位置要写包含index.php的那个主文件夹. 2.apache和thinkphp默认index.html是作为网站的默认首页,所以在浏览器端可以直接输入网址进 ...

  10. 【linux草鞋应用编程系列】_2_ 环境变量和进程控制

    一. 环境变量     应用程序在执行的时候,可能需要获取系统的环境变量,从而执行一些相应的操作.     在linux中有两种方法获取环境变量,分述如下.   1.通过main函数的参数获取环境变量 ...