首页 > 技术文章 > 参数化查询的另一个作用

blog-east 2017-05-21 21:07 原文

关于参数化暂时有发现有两个作用:

  1. 防止SQL注入.
  2. 执行计划的重用

 

这里我主要讲的是执行计划的重用这块.  

 

我们项目中写的SQLSQL SERVER中都会先生成一个唯一的hash值,然后根据这个hash值去缓存里面匹配对应的执行计划.

如果执行计划.不存在,则开始走生成执行计划的模块(注:这个模块是很耗效能的,以至于连微软都要把这个执行计划缓存)

 

所以我们在写SQL的过程中,如果有一个字符发生变动(即使是一个空格),都会导致hash的变动,进而会重新编译该SQL对应的执行计划

例如: 

 

 

 

上面这个图两者的sql作用是一样的,但是因为第1个SQL多了个空格,导致SQL Server重新编译了该sql语句

 

而我们日常开发中,有关客户端传递过来的值;,我们大部分都会用参数化,而项目里面直接写的,可能就会直接声明了, 这样就会导致下面这种情况→_→

直接声明:

  

在换个其他值测试:

再次执行,且观察执行计划可得知

可以得知我们手写的SQL是执行的SQL自动生成的执行计划,而非我们自己手写的执行计划。而使用次数作用到了手写的SQL上面。 

 

另一种方式,我们改用参数化

 

 换其他值测试:

 

 

上述的测试中:一个简单的非参数化的SQL就已经占用了16KB的内存,如果是多表联合查询或者复杂性的查询,会造成多少内存的浪费,因为原本用一个执行计划就搞定的,现在却多出了N个。

如果我们项目中大量的SQL都将固定的值写在sql里面,我们认为会只用一个SQL执行计划,实际上是SQL Server将我们的每个sql都编译了一个执行计划,在一定程度上增加了对性能和内存的消耗。

所以我们项目中如果是固定的值,也要提取到参数化里面,这样可以共享一个执行计划,节省性能以及内存的消耗。  

 

-------------华丽的分界线------------------

 

扩展: 上述的测试中,用参数化执行SQL的方式,我遇到过这种情况, 即如下:

 

使用了参数化却还是生成了两个执行计划,通过分析可发现是因为SQL Parameter没有传入长度所致, 调整输入长度后 测试如下: 

 

进一步可得知,如果参数化的时候不指定字段长度的话(我这里指定30是因为Address表中的City字段类型是:nvarchar(30)),也会在一定程度上造成多余执行计划的增长,进而造成内存的浪费。

 

结语:

 

当完全参数化的可能还会出现参数嗅探的问题,所以在使用的时候,还得具体情况具体分析。

 

目前有个问题想请教下,就是当非参数化的时候,SQL SERVER自动生成的执行计划是做什么用的?

>>这个部分感谢答复我问题的@MSSQL123大神,他解答了我对这个部分的疑惑,非常感谢:)

 

=================结束的分割线=====================

 

上面主要用的SQL如下:

 

清空执行计划:

DBCC FreeProcCache

查询上面结果的SQL

SELECT 

cp.usecounts as '使用次数' ,

cp.cacheobjtype as '缓存类型',

cp.objtype as '对象类型',

st.text as 'TSQL',

qp.query_plan as '执行计划',

cp.size_in_bytes as '执行计划占用空间(byte)'

FROM

sys.dm_exec_cached_plans cp

cross apply sys.dm_exec_sql_text(plan_handle) st

cross apply sys.dm_exec_query_plan(plan_handle) qp

 

 

推荐阅读