视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
SQLServer参数化查询大数据下的实践
2020-11-09 13:53:36 责编:小采
文档


身为一名小小的程序员,在日常开发中不可以避免的要和where in和like打交道,在大多数情况下我们传的参数不多简单做下单引号、敏感字符转义之后就直接拼进了SQL,执行查询,搞定。若有一天你不可避免的需要提高SQL的查询性能,需要一次性where in 几百、上千

  身为一名小小的程序员,在日常开发中不可以避免的要和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();

  }

下载本文
显示全文
专题