转载本文请联系DBA杂念公众号。SQLServer的查询条件中,IN中可以使用变量吗?如果可以的话,有什么需要注意的地方或限制吗?在回答这个问题之前,我们先看看这个例子:IFEXISTS(SELECT1FROMsys.objectsWHEREname='TEST'ANDtype='U')BEGINDROPTABLETEST;ENDGOCREATETABLETEST(IDINT,NAMEVARCHAR(16));GOINSERTINTOdbo.TESTSELECT1,'a'UNIONALLSELECT2,'b'UNIONALLSELECT3,'c'UNIONALLSELECT4,'a,b'UNIONALLSELECT5,'''b'',''c'''UNIONALLSELECT6,'''b';GO如下,如果查询条件中变量只有一个值,此时SQL正常。DECLARE@nameVARCHAR(16);SET@name='a';SELECT*FROMTESTWHEREnameIN(@name);GODECLARE@nameVARCHAR(16);SET@name='a,b';SELECT*FROMTESTWHEREnameIN(@name);如果我们是否要在查询条件IN中输入多个值?如果有这样的需求,一个变量包含b和c的值,现在用'b|c'作为条件传入,拆分成变量'b'和'c',我要找出name=b和name=c的记录,如下截图所示,SQL实际上并没有按照你的“想象/预测”找出对应的记录,而是检测到ID=5的记录DECLARE@name1VARCHAR(16);DECLARE@name2VARCHAR(16);SET@name1='b|c';SET@name2=REPLACE(@name1,'|',''',''')SELECT@name2SELECT*FROMTESTWHEREnameIN((''''+@name2+''''));下面的SQL也是同样的结果。DECLARE@name1VARCHAR(16);DECLARE@name2VARCHAR(16);SET@name1='b|c';SET@name2=''''+REPLACE(@name1,'|',''',''')+''''SELECT@name2SELECT*FROMTESTWHEREnameIN(@name2);为什么会出现这样的结果?查了很多官方文档,都没有看到关于这个问题的介绍和解释。如果一定要解释以上现象,那是因为SELECT*FROMTESTWHEREnameIN(@name2);实际上转化为SELECT*FROMTESTWHEREname=@name2;“假设”的逻辑运算。相反,进行了转换。为什么会这样转换呢?当然,这也是一种猜想。上面构造的例子,也是为了从侧面验证这个猜想。另外,上述两条SQL实际执行计划的参数列表(ParameterList)也侧面印证了这个猜想(如下截图所示)。如果执行计划解析成我们想要的结果,那么ParameterList应该是'b'和'c'解决方法:1:用动态SQL解决问题用动态SQL,好像没什么好说的,as如下例所示:DECLARE@sql_cmdNVARCHAR(max);DECLARE@nameVARCHAR(16);SET@name='b|c';SET@sql_cmd='SELECT*FROMTESTWHEREnameIN('''+REPLACE(@name,'|',''',''')+''');'EXECsp_executesql@sql_cmd;2:使用临时表或表变量本例中是将字符串拆分,放入临时表或表变量中,然后关联表,在IN里面使用子查询也是可以的。3:WithSTRING_SPLIT()DECLARE@nameVARCHAR(16);SET@name='b|c';SELECT*FROMtestWHEREnameIN(SELECTvalueFROMSTRING_SPLIT(@name,'|'))注意:STRING_SPLIT函数只有高版本支持,SQL支持Server2017或某些版本的SQLServer2016。4:使用XML函数解决问题DECLARE@nameVARCHAR(16);DECLARE@xml_paraXML;SET@name='b|c';SET@xml_para=CAST((''+REPLACE(@name,'|','')+'')ASXML);SELECT*FROMdbo.TESTWHERENAMEIN(SELECTA.value('.','varchar(max)')AS[Column]FROM@xml_para.nodes('A')ASFN(A));
