肥城龙山网络:专注肥城网站建设、网站开发、网页制作、软件开发等。 24小时服务热线
肥城龙山网络logo

肥城网站建设首页
网站建设
Website
客户案例
Case
软件开发
Advantages
 域名主机
Domain Hosting
 网站推广
E-mail
 服务&支持
Promotion
 联系我们
About Us
网站建设套餐

新闻中心
三层事物代码
利用token防止重复提交问题!!
使用layer.m弹出过程中微信缓存问题的解决办法
html常用特殊字符转义代码
iis 设置网站文件夹或文件读写 编辑 删除权限!!
sql去重复显示其他字段的方法总结
Sql Server参数化查询之where in和like实现详解


拼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, -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 = 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, 200) { Value = "rabbit%" });
    comm.ExecuteNonQuery();
}
复制代码

 

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

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

 
 
公司地址:肥城龙山公园园林局对过  网站备案/许可证号:鲁ICP备14032147号-2    
肥城龙山网络拥有多年网站建设及开发经验,从事网页设计、网站设计(全套)及制作等,因而网站制作的经验极为丰富。经营范围:肥城网站建设、网站制作、肥城网站设计等。


版权所有:肥城龙山网络科技中心