定义要与IN运算符一起使用的变量(T-SQL)


138

我有一个使用IN运算符的Transact-SQL查询。像这样:

select * from myTable where myColumn in (1,2,3,4)

有没有一种方法来定义一个变量来保存整个列表“(1,2,3,4)”?我应该如何定义它?

declare @myList {data type}
set @myList = (1,2,3,4)
select * from myTable where myColumn in @myList

7
此问题与“参数化SQL IN子句”问题不同。这个问题涉及本地T-SQL,另一个问题涉及C#。
Slogmeister Extraordinaire 2015年

Answers:


113
DECLARE @MyList TABLE (Value INT)
INSERT INTO @MyList VALUES (1)
INSERT INTO @MyList VALUES (2)
INSERT INTO @MyList VALUES (3)
INSERT INTO @MyList VALUES (4)

SELECT *
FROM MyTable
WHERE MyColumn IN (SELECT Value FROM @MyList)

47
DECLARE @mylist TABLE (Id int)
INSERT INTO @mylist
SELECT id FROM (VALUES (1),(2),(3),(4),(5)) AS tbl(id)

SELECT * FROM Mytable WHERE theColumn IN (select id from @mylist)

T-SQL说[Err] 42000 - [SQL Server]Must declare the scalar variable "@mylist".
Cees Timmerman 2014年

1
为您修复了此问题@Paul
Stefan Z Camilleri

5
您可以(VALUES (1),(2),(3),(4),(5))直接使用吗?
toddmo

这是满足我需求的最佳解决方案。我需要一个变量作为从Select中获取的ID的列表,因此这些值不是预先确定的。正是我所需要的。谢谢!
Lexi847942'9

12

有两种方法可以处理TSQL查询的动态csv列表:

1)使用内部选择

SELECT * FROM myTable WHERE myColumn in (SELECT id FROM myIdTable WHERE id > 10)

2)使用动态串联的TSQL

DECLARE @sql varchar(max)  
declare @list varchar(256)  
select @list = '1,2,3'  
SELECT @sql = 'SELECT * FROM myTable WHERE myColumn in (' + @list + ')'

exec sp_executeSQL @sql

3)可能的第三个选择是表变量。如果您有SQl Server 2005,则可以使用表变量。如果在Sql Server 2008上,您甚至可以将整个表变量作为参数传递给存储过程,并在联接中或IN子句中用作子选择。

DECLARE @list TABLE (Id INT)

INSERT INTO @list(Id)
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4


SELECT
    * 
FROM 
    myTable
    JOIN @list l ON myTable.myColumn = l.Id

SELECT
    * 
FROM 
    myTable
WHERE
    myColumn IN (SELECT Id FROM @list)

5
@ badbod99-那是一个概括,所有概括都是错误的:)我提供了替代方法
hollystyles

1
@Vilx-您是要设置变量@list吗?如果这样设置很好,但仅设置一个变量,则使用select可以在一个语句中填充多个变量。由于它们之间没有太多关系,所以我习惯于始终使用SELECT。
hollystyles

1
是的...非常笼统。您的替代方法更好。我的意思是,从SQL脚本中生成SQL通常会导致代码无法维护,存在注入攻击的风险以及其他麻烦。
badbod99,2009年

9

使用如下函数:

CREATE function [dbo].[list_to_table] (@list varchar(4000))
returns @tab table (item varchar(100))
begin

if CHARINDEX(',',@list) = 0 or CHARINDEX(',',@list) is null
begin
    insert into @tab (item) values (@list);
    return;
end


declare @c_pos int;
declare @n_pos int;
declare @l_pos int;

set @c_pos = 0;
set @n_pos = CHARINDEX(',',@list,@c_pos);

while @n_pos > 0
begin
    insert into @tab (item) values (SUBSTRING(@list,@c_pos+1,@n_pos - @c_pos-1));
    set @c_pos = @n_pos;
    set @l_pos = @n_pos;
    set @n_pos = CHARINDEX(',',@list,@c_pos+1);
end;

insert into @tab (item) values (SUBSTRING(@list,@l_pos+1,4000));

return;
end;

您可以使用函数返回的表进行内部联接,而不是使用like:

select * from table_1 where id in ('a','b','c')

变成

select * from table_1 a inner join [dbo].[list_to_table] ('a,b,c') b on (a.id = b.item)

在未索引的1M记录表中,第二个版本花费了大约一半的时间...

干杯


5
DECLARE @myList TABLE (Id BIGINT) INSERT INTO @myList(Id) VALUES (1),(2),(3),(4);
select * from myTable where myColumn in(select Id from @myList)

请注意,对于长清单或生产系统,不建议使用这种方式,因为它可能比简单的IN运算符someColumnName in (1,2,3,4)(使用8000多种商品进行测试)慢得多


4

不,没有这种类型。但是有一些选择:

  • 动态生成的查询(sp_executesql)
  • 临时表
  • 表类型变量(到列表最近的东西)
  • 创建一个XML字符串,然后将其转换为具有XML函数的表(确实很尴尬和环形交叉,除非您以XML开头)

这些都不是真正的优雅,但这是最好的。


4

@LukeH略有改进,无需重复“ INSERT INTO”:和@realPT的答案-无需进行SELECT:

DECLARE @MyList TABLE (Value INT) 
INSERT INTO @MyList VALUES (1),(2),(3),(4)

SELECT * FROM MyTable
WHERE MyColumn IN (SELECT Value FROM @MyList)

4

我知道这已经很老了,但是TSQL => 2016,您可以使用STRING_SPLIT:

DECLARE @InList varchar(255) = 'This;Is;My;List';

WITH InList (Item) AS (
    SELECT value FROM STRING_SPLIT(@InList, ';')
)

SELECT * 
FROM [Table]
WHERE [Item] IN (SELECT Tag FROM InList)

4

从SQL2017开始,您可以使用STRING_SPLIT并执行以下操作:

declare @myList nvarchar(MAX)
set @myList = '1,2,3,4'
select * from myTable where myColumn in (select value from STRING_SPLIT(@myList,','))

2

如果要在不使用第二张表的情况下执行此操作,则可以使用CAST进行LIKE比较:

DECLARE @myList varchar(15)
SET @myList = ',1,2,3,4,'

SELECT *
FROM myTable
WHERE @myList LIKE '%,' + CAST(myColumn AS varchar(15)) + ',%'

如果您要比较的字段已经是字符串,则不需要CAST。

将列匹配项和每个唯一值都用逗号括起来将确保完全匹配。否则,将在包含“,4,2,15,”的列表中找到值1



1

这使用PATINDEX来将表中的ID与非数字定界整数列表进行匹配。

-- Given a string @myList containing character delimited integers 
-- (supports any non digit delimiter)
DECLARE @myList VARCHAR(MAX) = '1,2,3,4,42'

SELECT * FROM [MyTable]
    WHERE 
        -- When the Id is at the leftmost position 
        -- (nothing to its left and anything to its right after a non digit char) 
        PATINDEX(CAST([Id] AS VARCHAR)+'[^0-9]%', @myList)>0 
        OR
        -- When the Id is at the rightmost position
        -- (anything to its left before a non digit char and nothing to its right) 
        PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR), @myList)>0
        OR
        -- When the Id is between two delimiters 
        -- (anything to its left and right after two non digit chars)
        PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR)+'[^0-9]%', @myList)>0
        OR
        -- When the Id is equal to the list
        -- (if there is only one Id in the list)
        CAST([Id] AS VARCHAR)=@myList

笔记:

  • 当转换为varchar并且未在括号中指定字节大小时,默认长度为30
  • %(通配符)将匹配零个或多个字符的任何字符串
  • ^(通配符)不匹配
  • [^ 0-9]将匹配任何非数字字符
  • PATINDEX是一个SQL标准函数,它返回字符串中模式的位置

0
DECLARE @StatusList varchar(MAX);
SET @StatusList='1,2,3,4';
DECLARE @Status SYS_INTEGERS;
INSERT INTO  @Status 
SELECT Value 
FROM dbo.SYS_SPLITTOINTEGERS_FN(@StatusList, ',');
SELECT Value From @Status;

5
如果您在那里描述代码,这将是一个更好的答案!
深卡卡

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.