本文转自;http://www.cnblogs.com/lzrabbit/archive/2012/04/22/2465313.html

文章导读

拼SQL实现where in查询

使用CHARINDEX或like实现where in 参数化

使用exec动态执行SQl实现where in 参数化

为每一个参数生成一个参数实现where in 参数化

使用临时表实现where in 参数化

like参数化查询

xml和DataTable传参

身为一名小小的程序猿,在日常开发中不可以避免的要和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(new SqlParameter("@Temp_Array", SqlDbType.VarChar, -) { 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, ) { Value = "rabbit%" });
comm.ExecuteNonQuery();
} using (SqlConnection conn = new SqlConnection(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, ) { Value = "rabbit%" });
comm.ExecuteNonQuery();
}

看到Tom.汤和蚊子额的评论 补充了下xml传参和tvp传参,并对6种方案做了个简单总结

Sql Server参数化查询之where in和like实现之xml和DataTable传参 

注:此文章属懒惰的肥兔原创,版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接:http://www.cnblogs.com/lzrabbit/archive/2012/04/22/2465313.html

若您觉得这篇文章还不错请点击下右下角的推荐,有了您的支持才能激发作者更大的写作热情,非常感谢。

如有问题,可以通过lzrabbit@126.com联系我。

[转]Sql Server参数化查询之where in和like实现详解的更多相关文章

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

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

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

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

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

    文章导读 拼SQL实现where in查询 使用CHARINDEX或like实现where in 参数化 使用exec动态执行SQl实现where in 参数化 为每一个参数生成一个参数实现where ...

  4. Sql Server参数化查询之where in和like实现详解 [转]

    文章导读 拼SQL实现where in查询 使用CHARINDEX或like实现where in 参数化 使用exec动态执行SQl实现where in 参数化 为每一个参数生成一个参数实现where ...

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

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

  6. Sql Server参数化查询之where in和like实现之xml和DataTable传参 (转)

    在上一篇Sql Server参数化查询之where in和like实现详解中介绍了在Sql Server使用参数化查询where in的几种实现方案,遗漏了xml和表值参数,这里做一个补充 文章导读 ...

  7. SQL Server 表的管理_关于事务操作的详解(案例代码)

    SQL Server 表的管理_关于事务操作的详解(案例代码) 1.概念 事务(transaction): 是将多个修改语句组合在一起的方法,这个方法中的所有语句只有全部执行才能正确完成功能.即要么全 ...

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

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

  9. 浅析Sql Server参数化查询

    说来惭愧,工作差不多4年了,直到前些日子被DBA找上门让我优化一个CPU占用很高的复杂SQL语句时,我才突然意识到了参数化查询的重要性. 相信有很多开发者和我一样对于参数化查询认识比较模糊,没有引起足 ...

随机推荐

  1. leetcode 之Reverse Linked List II(15)

    这题用需要非常细心,用头插法移动需要考虑先移动哪个,只需三个指针即可. ListNode *reverseList(ListNode *head, int m, int n) { ListNode d ...

  2. Python+Selenium 自动化实现实例-Css捕捉元素的几种方法

    #coding=utf-8 from selenium import webdriverimport timedriver = webdriver.Chrome()driver.get("h ...

  3. 导出数据到word

    打野的时候,碰到一个需求,导出简历信息. 两条思路: 第一条,直接画所有的表格,填充数据. 第二条,加载一个空的模板,然后填充数据. 因为导出的有格式的,所以最后选择了使用模板进行替换,然后填充数据. ...

  4. Genymotion的2个问题及解决方法

    问题一:Unable to connect to your virtual device 解决方法:基本都是内存不够导致的,在virtualbox中调整对应虚拟机的内存,确保任务管理器中的性能一项中剩 ...

  5. 原来Notepad++也有列模式(转)

    引子 一直在用Notepad++,小巧.顺手.偶尔使用UltraEdit来处理列模式:UE越来越大,启动时间太长,早都烦了.今天上网,偶然间看到,Notepad++也有列模式.拜拜UE,彻底删除你. ...

  6. destoon 部署到服务器如何修改密码使网站能正常访问登录

    1.根目录的config.inc.php  修改 db_pass 2.缓存文件 根目录/file/cache/module-2.php   修改 uc_dbpwd PS:不一定都在module-2.p ...

  7. php 如何写 mysql insert into 语句

    $sql="INSERT INTO moneytb (riqi,item,inout,cost,bz) VALUES ('$riqi','$item','$inout','$cost','$ ...

  8. vue中keep-alive

    vue2.0提供了一个keep-alive组件用来缓存组件,避免多次加载相应的组件,减少性能消耗 1.基本用法,缓存整个页面或组件 <keep-alive> <component&g ...

  9. TYVJ2002 扑克牌

    卢克生日那天,汉来找卢克玩扑克牌,玩着玩着汉觉得太没意思了,于是决定给卢克一个考验汉把一副扑克牌(54张)随机洗匀,倒扣着放成一摞.然后卢克从上往下一次翻开每张牌,每翻开一张黑桃,红桃,梅花或方块,就 ...

  10. URL获取并修改参数【转】

    function changeURLPar(url, ref, value) { var str = ""; if (url.indexOf('?') != -1) str = u ...