身为一名小小的程序猿,在日常开发中不可以避免的要和where in和like打交道,在大多数情况下我们传的参数不多简单做下单引号、敏感字符转义之后就直接拼进了SQL,执行查询,搞定。若有一天你不可避免的需要提高SQL的查询性能,需要一次性where in 几百、上千、甚至上万条数据时,参数化查询将是必然进行的选择。然而如何实现where in和like的参数化查询,是个让不少人头疼的问题。

where in 的参数化查询实现

首先说一下我们常用的办法,直接拼SQL实现,一般情况下都能满足需要

string userIds = "1,2,3,4"; using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; comm.CommandText = string.Format("select * from Users(nolock)where UserID in({0})", userIds); comm.ExecuteNonQuery(); }

需要参数化查询时进行的尝试,很显然如下这样执行SQL会报错错误

using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; comm.CommandText = "select* from Users(nolock) where UserID in(@UserID)"; comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" }); comm.ExecuteNonQuery(); }

很显然这样会报错误:在将 varchar 值 '1,2,3,4' 转换成数据类型 int 时失败,因为参数类型为字符串,where in时会把@UserID当做一个字符串来处理,相当于实际执行了如下语句

select * from Users(nolock) where UserID in('1,2,3,4')

若执行的语句为字符串类型的,SQL执行不会报错,当然也不会查询出任何结果

using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; comm.CommandText = "select* from Users(nolock) where UserName in(@UserName)"; comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, -1) { Value = "'john','dudu','rabbit'" }); comm.ExecuteNonQuery(); }

这样不会抱任何错误,也查不出想要的结果,因为这个@UserName被当做一个字符串来处理,实际相当于执行如下语句

select * from Users(nolock) where UserName in('''john'',''dudu'',''rabbit''')

由此相信大家对于为何简单的where in 传参无法得到正确的结果知道为什么了吧,下面我们来看一看如何实现正确的参数化执行where in,为了真正实现参数化where in 传参,很多淫才想到了各种替代方案

方案1,使用CHARINDEX或like 方法实现参数化查询,毫无疑问,这种方法成功了,而且成功的复用了查询计划,但同时也彻底的让查询索引失效(在此不探讨索引话题),造成的后果是全表扫描,如果表里数据量很大,百万级、千万级甚至更多,这样的写法将造成灾难性后果;如果数据量比较小、只想借助参数化实现防止SQL注入的话这样写也无可厚非,还是得看具体需求。(不推荐)

using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //使用CHARINDEX,实现参数化查询,可以复用查询计划,同时会使索引失效 comm.CommandText = "select * from Users(nolock)where CHARINDEX(','+ltrim(str(UserID))+',',','+@UserID+',')>0"; comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value ="1,2,3,4" }); comm.ExecuteNonQuery(); } using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //使用like,实现参数化查询,可以复用查询计划,同时会使索引失效 comm.CommandText = "select * from Users(nolock) where ','+@UserID+',' like'%,'+ltrim(str(UserID))+',%' "; comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" }); comm.ExecuteNonQuery(); }

方案2 使用exec动态执行SQL,这样的写法毫无疑问是很成功的,而且代码也比较优雅,也起到了防止SQL注入的作用,看上去很完美,不过这种写法和直接拼SQL执行没啥实质性的区别,查询计划没有得到复用,对于性能提升没任何帮助,颇有种脱了裤子放屁的感觉,但也不失为一种解决方案。(不推荐)

using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //使用exec动态执行SQL   //实际执行的查询计划为(@UserID varchar(max))select * from Users(nolock) where UserID in (1,2,3,4)   //不是预期的(@UserID varchar(max))exec('select * from Users(nolock) where UserID in ('+@UserID+')') comm.CommandText = "exec('select * from Users(nolock) where UserID in ('+@UserID+')')"; comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar,-1) { Value = "1,2,3,4" }); comm.ExecuteNonQuery(); }

方案3 为where in的每一个参数生成一个参数,写法上比较麻烦些,传输的参数个数有限制,最多2100个,可以根据需要使用此方案(推荐)

using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //为每一条数据添加一个参数 comm.CommandText = "select * from Users(nolock) where UserID in(@UserID1,@UserId2,@UserID3,@UserID4)"; comm.Parameters.AddRange( new SqlParameter[]{ new SqlParameter("@UserID1", SqlDbType.Int) { Value = 1}, new SqlParameter("@UserID2", SqlDbType.Int) { Value = 2}, new SqlParameter("@UserID3", SqlDbType.Int) { Value = 3}, new SqlParameter("@UserID4", SqlDbType.Int) { Value = 4} }); comm.ExecuteNonQuery(); }

方案4 使用临时表实现(也可以使用表变量性能上可能会更加好些),写法实现上比较繁琐些,可以根据需要写个通用的where in临时表查询的方法,以供不时之需,个人比较推崇这种写法,能够使查询计划得到复用而且对索引也能有效的利用,不过由于需要创建临时表,会带来额外的IO开销,若查询频率很高,每次的数据不多时还是建议使用方案3,若查询数据条数较多,尤其是上千条甚至上万条时,强烈建议使用此方案,可以带来巨大的性能提升(强烈推荐)

using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; string sql = @" declare @Temp_Variable varchar(max) create table #Temp_Table(Item varchar(max)) while(LEN(@Temp_Array) > 0) begin if(CHARINDEX(',',@Temp_Array) = 0) begin set @Temp_Variable = @Temp_Array set @Temp_Array = '' end else begin set @Temp_Variable = LEFT(@Temp_Array,CHARINDEX(',',@Temp_Array)-1) set @Temp_Array = RIGHT(@Temp_Array,LEN(@Temp_Array)-LEN(@Temp_Variable)-1) end insert into #Temp_Table(Item) values(@Temp_Variable) end select * from Users(nolock) where exists(select 1 from #Temp_Table(nolock) where #Temp_Table.Item=Users.UserID) drop table #Temp_Table"; comm.CommandText = sql; comm.Parameters.Add(newSqlParameter("@Temp_Array", SqlDbType.VarChar, -1) { Value = "1,2,3,4" }); comm.ExecuteNonQuery(); }

like参数化查询 like查询根据个人习惯将通配符写到参数值中或在SQL拼接都可,两种方法执行效果一样,在此不在详述

using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //将 % 写到参数值中comm.CommandText = "select * from Users(nolock) where UserName like @UserName"; comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, 200) { Value ="rabbit%" }); comm.ExecuteNonQuery(); } using (SqlConnection conn = newSqlConnection(connectionString)) { conn.Open(); SqlCommand comm = new SqlCommand(); comm.Connection = conn; //SQL中拼接 % comm.CommandText = "select * from Users(nolock) where UserName like @UserName+'%'"; comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, 200) { Value = "rabbit%" }); comm.ExecuteNonQuery(); }

where in 的参数化查询实现的更多相关文章

  1. SQL参数化查询自动生成SqlParameter列表

    string sql = @"INSERT INTO stu VALUES (@id,@name) "; 参数化查询是经常用到的,它可以有效防止SQL注入.但是需要手动去匹配参数@ ...

  2. Sql Server参数化查询之where in和like实现详解

    where in 的参数化查询实现 首先说一下我们常用的办法,直接拼SQL实现,一般情况下都能满足需要 string userIds = "1,2,3,4"; using (Sql ...

  3. 【转】浅析Sql Server参数化查询

    转载至: http://www.cnblogs.com/lzrabbit/archive/2012/04/21/2460978.html 错误认识1.不需要防止sql注入的地方无需参数化 参数化查询就 ...

  4. 【转】Sql Server参数化查询之where in和like实现之xml和DataTable传参

    转载至: http://www.cnblogs.com/lzrabbit/archive/2012/04/29/2475427.html 在上一篇Sql Server参数化查询之where in和li ...

  5. 【转】Sql Server参数化查询之where in和like实现详解

    转载至:http://www.cnblogs.com/lzrabbit/archive/2012/04/22/2465313.html 文章导读 拼SQL实现where in查询 使用CHARINDE ...

  6. Oracle参数化查询

    Oracle参数化查询默认是根据顺序绑定的 select * from table where name=:p1 and (select id from table2 where name=:p1); ...

  7. SQL 参数化查询 应用于 Like

    在sql 进行参数化查询的时候,使用like 语句和参数的时候,错误的写法:  Participant like '%@Participant%' ,这样在数据库为解析为 '%'participant ...

  8. ACCESS的参数化查询

    看论坛上还许多人问及ACCESS被注入的安全问题许多人解决的方法仍然是用Replace替换特殊字符,然而这样做也并没有起到太大做用今天我就把我用ACCESS参数化查询的一些方法和经验和大家分享希望对大 ...

  9. SQL Server 2008 R2——VC++ ADO 操作 参数化查询

    ==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...

  10. SQL参数化查询--最有效可预防SQL注入攻击的防御方式

    参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值. 在使用参 ...

随机推荐

  1. Hacklab WebIDE在线调试ESP32笔记

    目录 1.什么是Hacklab WebIDE 1.1 优势 1.2 趋势 2. 使用方法 2.1 功能介绍 2.2 编译第一个程序 2.3 搭建esp32的开发环境 2.4 建立开发板与云平台的连接 ...

  2. JavaMaven【三、常用指令】

    mvn compile --编译,编译后生成target文件,里面包含classes mvn test --执行test,测试后在target下生成reports文件夹,测试报告 mvn packag ...

  3. 【4】Zookeeper数据模型

    一.Znode节点是什么 1.1.概念   Znode节点是Zookeeper中数据模型中最小的数据单元.Zookeeper的数据模型是一颗树,由"/"进行分割路径.每个znode ...

  4. Odoo的 base 模型

    Odoo 内核中有一个base插件模块.它提供了 Odoo 应用所需的基本功能.然后有一组内置插件模块来提供标准产品中的官方应用和功能.base模块中包含两类模型: 信息仓库(Information ...

  5. Oracle笔记(一) Oracle简介及安装

    一.轨迹 二.Oracle简介 Oracle是现在全世界最大的数据库提供商,编程语言提供商,应用软件提供商,它的地位等价于微软的地位. Oracle在古希腊神话中被称为“神谕”,指的是上帝的宠儿,在中 ...

  6. 用Buildout来构建Python项目

    用Buildout来构建Python项目   什么是Buildout (Remixed by Matt Hamilton, original from http://xkcd.com/303) Bui ...

  7. pandas中的Series

    我们使用pandas经常会用到其下面的一个类:Series,那么这个类都有哪些方法呢?另外Series和DataFrame都继承了NDFrame这个类,df.to_sql()这个方法其实就是NDFra ...

  8. deep_learning_Dropout

    吴恩达深度学习笔记(十一)—— dropout正则化 主要内容: 一.dropout正则化的思想 二.dropout算法流程 三.dropout的优缺点 一.dropout正则化的思想 在神经网络中, ...

  9. 异步任务——AsyncTask的初步认识

    ProgressBar_test.class package com.example.administrator.ten_9; import android.app.Activity; import ...

  10. 【ios bug解决】 输入框聚焦时光标不显示

    解决办法:重写user-select样式 css:   user-select: text;-webkit-user-select:text;