`
smarttony2013
  • 浏览: 45945 次
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

浅析Sql Server参数化查询

 
阅读更多

说来惭愧,工作差不多4年了,直到前些日子被DBA找上门让我优化一个CPU占用很高的复杂SQL语句时,我才突然意识到了参数化查询的重要性。

相信有很多开发者和我一样对于参数化查询认识比较模糊,没有引起足够的重视

错误认识1.不需要防止sql注入的地方无需参数化
  参数化查询就是为了防止SQL注入用的,其它还有什么用途不知道、也不关心,原则上是能不用参数就不用参数,为啥?多麻烦,我只是做公司内部系统不用担心SQL注入风险,使用参数化查询不是给自己找麻烦,简简单单拼SQL,万事OK

错误认识2.参数化查询时是否指定参数类型、参数长度没什么区别
  以前也一直都觉的加与不加参数长度应该没有什么区别,仅是写法上的不同而已,而且觉得加参数类型和长度写法太麻烦,最近才明白其实两者不一样的,为了提高sql执行速度,请为SqlParameter参数加上SqlDbType和size属性,在参数化查询代码编写过程中很多开发者忽略了指定查询参数的类型,这将导致托管代码在执行过程中不能自动识别参数类型,进而对该字段内容进行全表扫描以确定参数类型并进行转换,消耗了不必要的查询性能所致。根据MSDN解释:如果未在size参数中显式设置Size,则从dbType参数的值推断出该大小。如果你认为上面的推断出该大小是指从SqlDbType类型推断,那你就错了,它实际上是从你传过来的参数的值来推断的,比如传递过来的值是"username",则size值为8,"username1",则size值为9。那么,不同的size值会引发什么样的结果呢?且经测试发现,size的值不同时,会导致数据库的执行计划不会重用,这样就会每次执行sql的时候重新生成新的执行计划,而浪费数据库执行时间。

下面来看具体测试

首先清空查询计划

DBCC FREEPROCCACHE

传值username,不指定参数长度,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username,不指定参数长度
    //查询计划为(@UserName varchar(8))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar) { Value = "username" });
    comm.ExecuteNonQuery();
}
复制代码

传值username1,不指定参数长度,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username1,不指定参数长度
    //查询计划为(@UserName varchar(9))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar) { Value = "username1" });
    comm.ExecuteNonQuery();
}
复制代码

传值username,指定参数长度为50,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username,指定参数长度为50
    //查询计划为(@UserName varchar(50))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar,50) { Value = "username" });
    comm.ExecuteNonQuery();
}
复制代码

传值username1,指定参数长度为50,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username1,指定参数长度为50
    //查询计划为(@UserName varchar(50))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar,50) { Value = "username1" });
    comm.ExecuteNonQuery();
}
复制代码

使用下面语句查看执行的查询计划

SELECT cacheobjtype,objtype,usecounts,sql FROM sys.syscacheobjects 
WHERE sql LIKE '%Users%'  and sql not like '%syscacheobjects%'

结果如下图所示

可以看到指定了参数长度的查询可以复用查询计划,而不指定参数长度的查询会根据具体传值而改变查询计划,从而造成性能的损失。

这里的指定参数长度仅指可变长数据类型,主要指varchar,nvarchar,char,nchar等,对于int,bigint,decimal,datetime等定长的值类型来说,无需指定(即便指定了也没有用),详见下面测试,UserID为int类型,无论长度指定为2、20、-1查询计划都完全一样为(@UserIDint)select*from Users where UserID=@UserID

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserID=@UserID";
    //传值 2,参数长度2
    //执行计划(@UserID int)select * from Users where UserID=@UserID
    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.Int, 2) { Value = 2 });
    comm.ExecuteNonQuery();
}
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserID=@UserID";
    //传值 2,参数长度20
    //执行计划(@UserID int)select * from Users where UserID=@UserID
    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.Int, 20) { Value = 2 });
    comm.ExecuteNonQuery();
}
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserID=@UserID";
    //传值 2,参数长度-1
    //执行计划(@UserID int)select * from Users where UserID=@UserID
    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.Int, -1) { Value = 2 });
    comm.ExecuteNonQuery();
}
复制代码

这里提一下,若要传值varchar(max)或nvarchar(max)类型怎么传,其实只要设定长度为-1即可

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //类型为varchar(max)时,指定参数长度为-1
    //查询计划为 (@UserName varchar(max) )select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar,-1) { Value = "username1" });
    comm.ExecuteNonQuery();
}
复制代码

当然了若是不使用参数化查询,直接拼接SQL,那样就更没有查询计划复用一说了,除非你每次拼的SQL都完全一样

总结,参数化查询意义及注意点

1.可以防止SQL注入

2.可以提高查询性能(主要是可以复用查询计划),这点在数据量较大时尤为重要

3.参数化查询参数类型为可变长度时(varchar,nvarchar,char等)请指定参数类型及长度,若为值类型(int,bigint,decimal,datetime等)则仅指定参数类型即可

4.传值为varchar(max)或者nvarchar(max)时,参数长度指定为-1即可

  

注意:此文章属博主原创,转载请注明作者信息和原始链接,谢谢合作。如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注 懒惰的肥兔】。因为,我的写作热情也离不开您的肯定支持。感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是懒惰的肥兔。
分享到:
评论

相关推荐

    浅析SqlServer参数化查询[参考].pdf

    SqlServer 参数化查询详解 在本文中,我们将深入浅析 Sql Server 参数化查询的重要性,Dispelling 两个常见的误解,并探讨参数化查询的正确编写方式,以提高 SQL 执行速度。 错误认识 1: 参数化查询仅用于防止 SQL...

    浅析SQL Server参数化查询

    SQL Server参数化查询是一种编程技术,它允许开发者创建可重用的SQL语句,其中的变量部分通过参数来传递。这种技术对优化查询性能、防止SQL注入攻击以及提高代码的可读性和可维护性有着重要作用。 错误认识1:在...

    浅析SQL Server数据库的安全和管理策略.pdf

    2. 参数化查询:在编写SQL语句时,应当尽量使用参数化查询来防止SQL注入。 3. 过滤用户输入:对于来自用户的所有输入,都需要进行严格的验证和过滤,以确保其合法性。 4. 最小权限原则:对于数据库中的数据和对象...

    浅析SQL Server中的执行计划缓存(下)

    总的来说,理解SQL Server执行计划缓存的工作原理,掌握参数化查询、合理使用`RECOMPILE`和`OPTIMIZE FOR`等技术,以及关注缓存性能指标,都是提升数据库性能的关键步骤。通过对这些知识点的深入研究和实践,可以...

    浅析SQL查询语句的优化策略.pdf

    3. 使用参数化查询:参数化查询可以减少SQL注入的风险,并且能够提高查询效率,因为它们通常会利用缓存的执行计划。 4. 优化子查询:子查询虽然方便,但有时候可能会影响查询性能,特别是在子查询中使用聚合函数,...

    浅析SQLServer中的Scanf与Printf

    在示例中,`xp_sprintf`创建了一个SQL查询语句,该语句根据传递的参数选择`sysobjects`表中的特定列,并根据`name`列的值筛选记录。`xp_sprintf`接受一个输出变量`@s`以及一系列要格式化的参数,例如列名、表名、...

    浅析Delphi中ADO与SQL Server 2000数据库的连接.pdf

    开发者需要考虑使用参数化查询来防止SQL注入攻击,同时还要确保连接字符串和敏感信息的安全。 参考文献中提及的“J.[M]”可能代表了期刊或书籍,这表明文章撰写者参考了相关的专业文献资料,对于Delphi数据库开发来...

    SQL Server存储过程浅析.pdf

    SQL Server存储过程是数据库编程中的一组Transact-SQL语句,它们经过编译后存储在一起,并作为单元执行。它们在服务器上创建和运行,减少了数据引擎的翻译工作,因此能提高执行效率。存储过程第一次运行可能较慢,...

    浅析SQL数据库修复技术.pdf

    当SQL Server数据库由于各种原因出现无法访问等故障时,是否能够及时修复和恢复数据是保证业务连续性和数据安全的关键。 首先,文章指出数据库损坏通常是由于硬件故障、不当的关闭操作、病毒攻击或其他未知因素引起...

    SQL Server2005数据库镜像技术的工作机制浅析.pdf

    SQL Server2005数据库镜像技术是该数据库管理系统中用于提高数据可用性、可靠性和灾难恢复能力的一种重要功能。它的核心思想是在两个或多个服务器之间创建数据库的副本来保证数据在发生故障时的快速恢复,以及在日常...

    浅析ASP网站中SQL注入的防范措施.pdf

    这样即使攻击者提交了恶意的SQL片段,由于没有正确的参数化,这些片段也不会被数据库引擎执行。 4. 对敏感信息进行加密:在程序中对用户密码等敏感信息进行加密处理。一般采用没有反向算法的md5函数进行加密。为了...

    深入浅析.NET应用程序SQL注入

    1. **参数化查询**:使用参数化查询是防止SQL注入最有效的方法。在.NET中,我们可以使用`SqlCommand`类的`Parameters`集合来定义参数,如下所示: ```csharp string sql = "SELECT COUNT(*) FROM admin WHERE ...

    浅析基于asp.net的网站安全漏洞及防范

    1. 使用参数化查询,确保用户输入的数据不会被解释为SQL命令的一部分。 2. 对所有用户输入进行严格的过滤和验证,避免特殊字符导致的SQL注入风险。 3. 限制数据库账户的权限,仅提供执行查询所需的最低权限。 #####...

    Oracle_TNS浅析.doc

    ### Oracle TNS浅析 #### 一、Oracle TNS简介 **TNS( Transparent Network Substrate)** 是Oracle Net的一部分,主要用于管理和配置Oracle数据库与客户端之间的连接。它为客户端提供了透明的网络通信机制,使得...

    Mysql+binlog浅析

    接下来,文档会详细解释如何启用和配置binlog,包括如何设置`server-id`以区分不同的MySQL实例,以及如何配置binlog的I/O线程和SQL线程以实现主从复制。还会介绍如何使用binlog进行数据恢复,如通过`mysqlbinlog`...

    VC与Labview、Matlab编程论文资料[2].rar

    VC_SQLServer和Matlab混合编程管理仿真数据.pdf VC_下MSComm控件的串口通信方法.pdf VC_与Matlab混合编程技术应用分析.pdf VC_与MATLAB混合编程的实现方法.pdf VC_与MATLAB混合编程研究及开发实例.pdf VC_与OpenGL...

Global site tag (gtag.js) - Google Analytics