参数化查询的另一个作用
关于参数化暂时有发现有两个作用:
- 防止SQL注入.
- 执行计划的重用
这里我主要讲的是执行计划的重用这块.
我们项目中写的SQL在SQL 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