经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MS SQL Server » 查看文章
SQL Server解惑——查询条件IN中能否使用变量
来源:cnblogs  作者:潇湘隐者  时间:2021/1/18 16:22:11  对本文有异议

在SQL Server的查询条件中,能否在IN里面使用变量呢? 如果可以的话,有没有需要注意的地方或一些限制呢?在回答这个问题前,我们先来看看这个例子:

 

  1. IF EXISTS (SELECT 1 FROM sys.objects WHERE name='TEST' AND type='U')
  1. BEGIN
  1.     DROP TABLE TEST;
  1. END
  1. GO
  1. CREATE TABLE TEST ( ID INT, NAME VARCHAR(16) );
  1. GO
  1.  
  1. INSERT INTO dbo.TEST
  1. SELECT 1, 'a'  UNION ALL
  1. SELECT 2, 'b'  UNION ALL
  1. SELECT 3, 'c'  UNION ALL
  1. SELECT 4, 'a,b'UNION ALL
  1. SELECT 5, '''b'',''c''' UNION ALL
  1. SELECT 6, '''b';
  1. GO

 

clip_image001

 

如下所示,如果查询条件里面,变量只有一个值,此时SQL是正常的。 

  1. DECLARE @name VARCHAR(16);
  1. SET @name='a';
  1.  
  1. SELECT * FROM TEST WHERE name IN (@name);
  1. GO
  1.  
  1. DECLARE @name VARCHAR(16);
  1. SET @name='a,b';
  1.  
  1. SELECT * FROM TEST WHERE name IN (@name);
  1. GO

 

如果我们想在查询条件IN里面输入多个值呢?假如有这样的一个需求,一个变量里面包含b和c的值,现在用'b|c作为条件传入,对其进行拆分为变量'b'和'c', 想查出name=b 和name=c的记录,如下截图所示,SQL其实并没有按你所设想/预想的查出对应记录,而是将ID=5的记录查出来了

 

  1. DECLARE @name1 VARCHAR(16);
  1. DECLARE @name2 VARCHAR(16);
  1. SET @name1='b|c';
  1. SET @name2=REPLACE(@name1,'|',''',''')
  1. SELECT @name2
  1.  
  1. SELECT * FROM TEST WHERE name IN (('''' + @name2 + ''''));

 

 

clip_image002

 

下面这个SQL也是同样的结果。 

 

  1. DECLARE @name1 VARCHAR(16);
  1. DECLARE @name2 VARCHAR(16);
  1. SET @name1='b|c';
  1. SET @name2='''' + REPLACE(@name1,'|',''',''') +''''
  1. SELECT @name2
  1.  
  1. SELECT * FROM TEST WHERE name IN (@name2 );

 

为什么出现了这样的结果呢? 查了大量的官方文档,没有看到关于这个问题的介绍和解释。如果一定要解释上面现象的情况的话,那么是因为SELECT * FROM TEST WHERE name IN (@name2 ); 其实转化为了SELECT * FROM TEST WHERE name =@name2; 也就是说,上面SQL并不会按你所设想的逻辑运算。而是做了一个转换,为什么说是这样的一个转换呢? 当然这也是一个猜想,上面构造的例子也是为了侧面验证这个猜想,另外,上面两个SQL实际执行计划的参数列表(Parameter List)也侧面印证了这个猜想。如果执行计划解析成我们想要的结果,那么Parameter List应该是'b' 和c'

 

 

 

clip_image003

 

clip_image004

 

 

 

解决方案:

 

1:使用动态SQL

 

使用动态SQL解决问题,似乎没啥好说的,如下例子所示:

 

  1. DECLARE @sql_cmd NVARCHAR(max);
  1. DECLARE @name VARCHAR(16);
  1.  
  1. SET @name='b|c';
  1. SET @sql_cmd='SELECT * FROM TEST WHERE name IN (''' + REPLACE(@name,'|',''',''') +''');'
  1.  
  1. EXEC sp_executesql @sql_cmd;

 

clip_image005

  

2:使用临时表或表变量

 

    以这个例子来说,就是将字符串拆分,放入临时表或表变量,然后关联表也好,在IN里面使用子查询也OK。

 

3:借助STRING_SPLIT() 

  1. DECLARE @name VARCHAR(16);
  1.  
  1. SET @name='b|c';
  1. SELECT *FROM  test WHERE name IN (SELECT value FROM STRING_SPLIT(@name, '|'))

 

clip_image006

 

注意STRING_SPLIT函数只有较高版本才支持,SQL Server 2017或SQL Server 2016部分版本支持。

 

4:借助XML函数来解决问题 

  1. DECLARE @name VARCHAR(16);
  1. DECLARE @xml_para XML;
  1.  
  1. SET @name = 'b|c';
  1. SET @xml_para = CAST(( '<A>' + REPLACE(@name, '|', '</A><A>') + '</A>' ) AS XML);
  1.  
  1.  
  1. SELECT  *
  1. FROM    dbo.TEST
  1. WHERE   NAME IN ( SELECT    A.value('.', 'varchar(max)') AS [Column]
  1.                   FROM      @xml_para.nodes('A') AS FN ( A ) );

 

clip_image007

 

原文链接:http://www.cnblogs.com/kerrycode/p/14291064.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号