你的SQL语句的参数化总是个好想法。使用参数化SQL语句你不会污染你的计划缓存——错!!!在这篇文章里我想向你展示下用参数化SQL语句就可以污染你的计划缓存,这是非常简单的!

ADO.NET-AddWithValue

ADO.NET是实现像SQL Server关系数据库数据访问的.NET框架的组成——有一些严重的副作用。不要误解我——只要你正确使用,ADO.NET一直很棒。你马上就会看到,它很容易被错误使用。我们来看下面实现SQL语句执行的C#代码。

 for (int i = ; i <= ; i++)
{
val += i.ToString(); cmd = new SqlCommand(
"SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber",
cnn);
cmd.Parameters.AddWithValue("@CarrierTrackingNumber", val);
SqlDataReader reader = cmd.ExecuteReader();
reader.Close();
}

我们是聪明的开发者,因此SQL语句本身被参数化,因为ADO.NET框架是地球上最棒的框架,我们使用System.Data.SqlClient.SqlParameterCollection类的AddWithValue方法来提供实际的参数值。我在WHLIE循环里运行那个SQL语句100次,总用不同长度赋予参数值。在Sales.SalesOrderDetail表里CarrierTrackingNumber列定义为NVARCHAR(25)。因此我们可以在基于我们提供的不同字符长度上有上至25个不同数据类型的参数。现在让我们检查下我们SQL语句执行后的计划缓存。

 SELECT
st.text,
cp.*
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
GO

现在事情变得有点疯狂:在计划缓存里我们存储了100个不同的执行计划!

对于每个可能的数据类型参数都有1个执行计划——即使当数据类型是NVACHAR(25)AddWithValue方法非常,非常邪恶:基于你提供的参数值派生出数据类型。永远不要使用它!

ADO.NET – SqlDbType.VarChar

因为从我们的错误中我们学到了,现在我们知道ADO.NET的AddWithValue方法的副作用——我们不再用它。现在让我们重写我们的C#程序代码,如下所示定义一个显示的参数数据类型:

 for (int i = ; i <= ; i++)
{
val += i.ToString(); cmd = new SqlCommand(
"SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber",
cnn);
cmd.Parameters.Add(new SqlParameter("@CarrierTrackingNumber", SqlDbType.VarChar));
cmd.Parameters["@CarrierTrackingNumber"].Value = val;
SqlDataReader reader = cmd.ExecuteReader();
reader.Close();
}

从代码里你可以看到,ADO.NET现在不能派生参数数据类型了,因为我们已经指定了SqlDbType.Varchar数据类型。让我们再次执行这个SQL语句100次并再次检查下计划缓存:

没有啥改变。问题还是一样:在计划缓存里我们还有100个不一样的的执行计划。现在的问题是ADO.NET只强制数据类型(SqlDbType.VarChar),但不是数据类型的"长度"。有100个不同的长度在计划缓存里你就有100个不同的执行计划。

如果你在你的ADO.NET代码里显式指定参数数据类型,你也要指定它的长度!现在我们来看下一些修正的C#代码。

 for (int i = ; i <= ; i++)
{
val += i.ToString(); cmd = new SqlCommand(
"SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber",
cnn);
cmd.Parameters.Add(new SqlParameter("@CarrierTrackingNumber", SqlDbType.VarChar, ));
cmd.Parameters["@CarrierTrackingNumber"].Value = val;
SqlDataReader reader = cmd.ExecuteReader();
reader.Close();
}

这次我也指定了数据类型的长度——这里是100,现在当我们再次执行SQL语句100次时,最后我们在计划缓存里以1个执行计划且重用了100次来完美收工。这是从SQL Server角度的最终目标。

小结

寓意:ADO.NET是个很棒的数据访问框架,它提供你有用的功能(例如AddWithValue方法),当从SQL Server角度来说你真的要考虑下你在做什么。当你使用参数化SQL语句时,你要尽量显式:你必须地冠以参数值的实际数据类型,还有你想要的获得数据类型长度。

感谢关注!

参考文章:

https://www.sqlpassion.at/archive/2015/07/20/how-to-pollute-your-plan-cache-with-parameterized-sql-statements/

如何用参数化SQL语句污染你的计划缓存的更多相关文章

  1. 在ADO.NET中使用参数化SQL语句访问不同数据库时的差异

    在ADO.NET中经常需要跟各种数据库打交道,在不实用存储过程的情况下,使用参数化SQL语句一定程度上可以防止SQL注入,同时对一些较难赋值的字段(如在SQL Server中Image字段,在Orac ...

  2. 参数化SQL语句

    避免SQL注入的方法有两种:一是所有的SQL语句都存放在存储过程中,这样不但可以避免SQL注入,还能提高一些性能,并且存储过程可以由专门的数据库管理员(DBA)编写和集中管理,不过这种做法有时候针对相 ...

  3. SQL Server参数化SQL语句中的like和in查询的语法(C#)

    sql语句进行 like和in 参数化,按照正常的方式是无法实现的 我们一般的思维是: Like参数化查询:string sqlstmt = "select * from users whe ...

  4. 查看SQL语句的真实执行计划

    DBMS_XPLAN包中display_cursor函数不同于display函数,display_cursor用于显示SQL语句的真实的执行计划,在大多数情况下,显示真实的执行计划有助于更好的分析SQ ...

  5. MySQL的EXPLAIN命令用于SQL语句的查询执行计划

    MySQL的EXPLAIN命令用于SQL语句的查询执行计划(QEP).这条命令的输出结果能够让我们了解MySQL 优化器是如何执行SQL 语句的.这条命令并没有提供任何调整建议,但它能够提供重要的信息 ...

  6. 谈一谈SQL Server中的执行计划缓存(上)

    简介 我们平时所写的SQL语句本质只是获取数据的逻辑,而不是获取数据的物理路径.当我们写的SQL语句传到SQL Server的时候,查询分析器会将语句依次进行解析(Parse).绑定(Bind).查询 ...

  7. 浅析SQL Server中的执行计划缓存(上)

    简介 我们平时所写的SQL语句本质只是获取数据的逻辑,而不是获取数据的物理路径.当我们写的SQL语句传到SQL Server的时候,查询分析器会将语句依次进行解析(Parse).绑定(Bind).查询 ...

  8. 利用IFormattable接口自动参数化Sql语句

    提要 string.Format("{0},{1}",a,b)的用法大家都不陌生了,在很多项目中都会发现很多sql语句存在这样拼接的问题,这种做法很多"懒"程序 ...

  9. C# 参数化SQL语句中的like和in

    在写项目的时候遇到一个问题,sql 语句进行 like in 参数化,按照正常的方式是无法实现的我们一般的思维是: Like 参数:string strSql = "select * fro ...

随机推荐

  1. 了解 JavaScript (5)– 翻转器(rollover)

    用 JavaScript 最常用的效果就是,当用户将鼠标移动到图片上时,会改变网页上的图像,这样页面就能对用户的操作及时作出反应,这种称为 翻转器(rollover)效果很容易实现,而且有很多应用场合 ...

  2. ios取证

    摘录自:<IOS取证实战> Andrew Hoog著 viaForensics公司,今年已经募集到1600万创业基金 2007-2011年初,AT&T是在美国唯一为iphone提供 ...

  3. 从零开始--系统深入学习IOS(使用Swift---带链接)

    这是一篇面向IOS新手的文档.同时提供一些系统知识的链接,让你系统学习IOS.它提供一些信息帮助你采用技术和编程接口来开发苹果软件产品,本人不保证会在将来更新.学习它,需要你掌握一些基本的编程知识 1 ...

  4. Turn off swi-prolog protocol output of ANSI terminal control sequences

    To save a record of program execution in prolog, we use the special predicates: protocol and noproto ...

  5. mvc 返回 xml

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Tex ...

  6. 【cocos2d-x 手游研发小技巧(8)通讯的数据压缩与解压 】

    今天说一下手机游戏通讯协议中的数据问题,大量的数据将给服务器端和客户端带来很大的压力,一般来说. 转载请注明出处:http://www.cnblogs.com/zisou/p/cocos2dxJQ-8 ...

  7. 树莓派保卫战--防止SSH暴力破解

    自己用树莓派搭建了个小server,用了很长时间了,最近查看log发现有很多SSH登陆失败,瞬间心就碎了,一直没关心小派的安全问题,怪我咯! 马上行动,首先研究下log:/var/log/auth.l ...

  8. saiku 无密码登陆

    公司想要使用saiku,希望没有密码直接可以使用,这样可以直接以iframe的形式嵌套到其他的系统中. 在网上搜索了很多,大致类似这篇博客介绍的:http://www.cnblogs.com/aviv ...

  9. Windows batch,echo到文件不成功,只打印出ECHO is on.

    jenkins 执行Windows batch command的时候,如果想要读写文件,echo到文件不成功. bat 代码如下: set ctime=%date%_%time% echo %ctim ...

  10. 使用word和pdf进行仿书编辑的经验

    一.问题的提出:    一本书扫描好,要将书中的图片转换为文字版的word文档.二.问题的分析:    1.文字的提取    2.文字的编排三.问题的解决    1.如果用的是Adobe Acroba ...