【问题标题】:Set variable with multiple values and use IN [duplicate]设置具有多个值的变量并使用 IN [重复]
【发布时间】:2011-11-17 21:10:47
【问题描述】:

可能重复:
Parameterizing a SQL IN clause?

在 SQL Server 中,我想为此做一些事情......

DECLARE @Values varchar(1000)

SET @Values = 'A, B, C'

SELECT
  blah
FROM 
  foo
WHERE
  myField IN (@Values)

这是否可能或如何实现?

【问题讨论】:

标签: sql-server tsql


【解决方案1】:

你需要一个表变量:

declare @values table
(
    Value varchar(1000)
)

insert into @values values ('A')
insert into @values values ('B')
insert into @values values ('C')

select blah
from foo
where myField in (select value from @values)

【讨论】:

  • 仅供参考,在 SQL Server 2008 及更高版本中,以下语法对于将多个显式值插入表变量中也是有效的:`insert into @values values ('A'),('B') ,('C')
  • 警示轶事:我最近做了这样的事情来使查询更易于维护……并在看到执行时间增加近 2000% 后迅速扭转了进程,从不到 800 毫秒超过 16,000 毫秒。粗略调查显示,每个in (select …) 都涉及表扫描;向表变量添加主键改善了这种情况,但还远远不够。当然是 YMMV。
  • SQL Server 2016 或更高版本您可以使用字符串拆分函数 DECLARE @Values varchar(1000) = 'A,B,C,D,E' SELECT * FROM WHERE ColumnName IN (SELECT VALUE FROM STRING_SPLIT( @Values,','))
【解决方案2】:

最好是shouldn't be splitting strings in T-SQL at all

除非发生这种变化,否则在 SQL Server 2016 之前的旧版本上,创建一个拆分函数:

CREATE FUNCTION dbo.SplitStrings
(
    @List      nvarchar(max), 
    @Delimiter nvarchar(2)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
  RETURN ( WITH x(x) AS
    (
      SELECT CONVERT(xml, N'<root><i>' 
        + REPLACE(@List, @Delimiter, N'</i><i>') 
        + N'</i></root>')
    )
    SELECT Item = LTRIM(RTRIM(i.i.value(N'.',N'nvarchar(max)')))
      FROM x CROSS APPLY x.nodes(N'//root/i') AS i(i)
  );
GO

现在你可以说:

DECLARE @Values varchar(1000);

SET @Values = 'A, B, C';

SELECT blah
  FROM dbo.foo
  INNER JOIN dbo.SplitStrings(@Values, ',') AS s
    ON s.Item = foo.myField;

在 SQL Server 2016 或更高版本(或 Azure SQL 数据库)上,它是 much simpler and more efficient,但是您必须手动应用 LTRIM() 才能去掉任何前导空格:

DECLARE @Values varchar(1000) = 'A, B, C';

SELECT blah
  FROM dbo.foo
  INNER JOIN STRING_SPLIT(@Values, ',') AS s
    ON LTRIM(s.value) = foo.myField;

【讨论】:

  • 当我尝试这个时它只返回@values 中的第一个值。例如,如果我写了 Set Values = 'A, B, C';这仅返回 A,如果我将其重新安排为 Set Values = 'B, A, C' 它只会给我返回 B....
  • @Pr0x1mo 听起来你在某处声明了varchar 却没有指定长度?你能在某个地方展示一个复制品吗?因为这个解决方案对我有用。
  • 使用 CLSInc 声明用户 VARCHAR(1000); SET用户='ABBY SIMON,AMY SALAS'; SELECT [whoami] ,[TRCK_DATE] , ([STIME]) as 'Start time' ,row_number() over(partition by whoami, trck_date order by stime) as 'row check' - 进入#stime FROM [CLSInc].[ dbo].[TRACKUSR] 内部连接 ​​dbo.SplitStrings(@User, ',') as s on s.item = whoami where trck_date between '05/01/2019' 和 '05/02/2019' group by whoami, trck_date , stime 按 STIME 排序
  • @Pr0x1mo 试试ON LTRIM(s.item) = whoamiON s.item = ' ' + whoami。您也可以将函数中的RTRIM(...) 更改为LTRIM(RTRIM(...))。您可以暂时使用RIGHT OUTER JOIN 来证明该函数有多行出来。您最初的投诉表明该函数仅返回一行;现在很明显他们被加入过滤掉了。
  • 哦,该死的。非常感谢!
【解决方案3】:

使用临时表或表变量,例如

select 'A' as [value]
into #tmp
union
select 'B'
union 
select 'C'

然后

SELECT   
blah 
FROM    foo 
WHERE   myField IN (select [value] from #tmp) 

SELECT   
f.blah 
FROM foo f INNER JOIN #tmp t ON f.myField = t.[value]

【讨论】:

    猜你喜欢
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-12
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    • 1970-01-01
    相关资源
    最近更新 更多