SQL变量用于保存整数列表


175

我正在尝试调试其他人的SQL报表,并将基础报表查询放入SQL 2012的查询窗口中。

报告要求的参数之一是整数列表。这是通过多选下拉框在报表上实现的。报表的基础查询在where子句中使用此整数列表,例如

select *
from TabA
where TabA.ID in (@listOfIDs)

我不想修改正在调试的查询,但是无法弄清楚如何在SQL Server上创建一个变量来保存这种类型的数据以对其进行测试。

例如

declare @listOfIDs int
set listOfIDs  = 1,2,3,4

没有可以容纳整数列表的数据类型,那么如何在SQL Server上使用与报表相同的值运行报表查询?


1
我知道我已经使用TVP表值参数来插入数据,但是现在确定它是否可以在哪里使用。续集?
狗仔队

2
措辞很好的问题。+1
RayLoveless

Answers:


224

表变量

declare @listOfIDs table (id int);
insert @listOfIDs(id) values(1),(2),(3);    

select *
from TabA
where TabA.ID in (select id from @listOfIDs)

要么

declare @listOfIDs varchar(1000);
SET @listOfIDs = ',1,2,3,'; --in this solution need put coma on begin and end

select *
from TabA
where charindex(',' + CAST(TabA.ID as nvarchar(20)) + ',', @listOfIDs) > 0

2
谢谢你,但同样需要我重写查询中读取变量的方式。我必须保持不变。
ErickTreetops

3
如果您不知道ID是什么并且来自查询,该怎么办?示例:SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)此查询将返回多个ID,并且我收到一条错误消息,指出子查询返回了多个结果,并且不允许这样做。无论如何,如果来自子查询的ID会创建一个变量来存储数组?
拉斐尔·莫雷拉

1
我尝试了第二种选择,它可以减少记录数量。当我增加Ids的数量时,会出现TimeOut错误。可能演员表妨碍了表演。
用户M

不是原始问题的答案,而是我没有问的问题的答案,所以我很好。我传入了一个List <int>作为参数,但是想要创建一个本地变量以在SSMS中进行测试。是杀手
韦德·哈特勒

好的答案,为我节省了很多时间
Alfredo

35

假设变量类似于:

CREATE TYPE [dbo].[IntList] AS TABLE(
[Value] [int] NOT NULL
)

而存储过程正以这种形式使用它:

ALTER Procedure [dbo].[GetFooByIds]
    @Ids [IntList] ReadOnly
As 

您可以创建IntList并按以下方式调用过程:

Declare @IDs IntList;
Insert Into @IDs Select Id From dbo.{TableThatHasIds}
Where Id In (111, 222, 333, 444)
Exec [dbo].[GetFooByIds] @IDs

或者,如果您自己提供IntList

DECLARE @listOfIDs dbo.IntList
INSERT INTO @listofIDs VALUES (1),(35),(118);

17

没错,SQL-Server中没有可以容纳整数列表的数据类型。但是您可以做的是将整数列表存储为字符串。

DECLARE @listOfIDs varchar(8000);
SET @listOfIDs = '1,2,3,4';

然后,您可以将字符串拆分为单独的整数值,并将它们放入表中。您的过程可能已经这样做了。

您还可以使用动态查询来达到相同的结果:

DECLARE @SQL nvarchar(8000);

SET @SQL = 'SELECT * FROM TabA WHERE TabA.ID IN (' + @listOfIDs + ')';
EXECUTE (@SQL);

谢谢,但是再次需要修改一个我不允许的查询。
ErickTreetops

2
如果有人使用它,请注意,如果@listOfIDs是用户提供的字符串参数,则这很容易受到SQL注入的攻击。根据您的应用程序体系结构,这可能是问题,也可能不是问题。
Rogala '17

@Rogala同意,用户将需要根据需要进行自己的卫生处理。
Möoz

1
@Möoz我建议在您的答案中添加注释以反映这一点。并非所有人都知道,他们只是复制并粘贴解决方案而无需考虑后果。动态SQL非常危险,我避免那样的困扰。
罗加拉

@Möoz另外,我没有否决您的答案,但是请您花几分钟时间检查一下我的答案吗?STRING_SPLIT函数非常不错,我想您会一臂之力!!
罗加拉(Rogala)

6

对于SQL Server 2016+和Azure SQL数据库,添加了STRING_SPLIT函数,它将完美解决此问题。这里是文档:https : //docs.microsoft.com/zh-cn/sql/t-sql/functions/string-split-transact-sql

这是一个例子:

/*List of ids in a comma delimited string
  Note: the ') WAITFOR DELAY ''00:00:02''' is a way to verify that your script 
        doesn't allow for SQL injection*/
DECLARE @listOfIds VARCHAR(MAX) = '1,3,a,10.1,) WAITFOR DELAY ''00:00:02''';

--Make sure the temp table was dropped before trying to create it
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable;

--Create example reference table
CREATE TABLE #MyTable
([Id] INT NOT NULL);

--Populate the reference table
DECLARE @i INT = 1;
WHILE(@i <= 10)
BEGIN
    INSERT INTO #MyTable
    SELECT @i;

    SET @i = @i + 1;
END

/*Find all the values
  Note: I silently ignore the values that are not integers*/
SELECT t.[Id]
FROM #MyTable as t
    INNER JOIN 
        (SELECT value as [Id] 
        FROM STRING_SPLIT(@listOfIds, ',')
        WHERE ISNUMERIC(value) = 1 /*Make sure it is numeric*/
            AND ROUND(value,0) = value /*Make sure it is an integer*/) as ids
    ON t.[Id] = ids.[Id];

--Clean-up
DROP TABLE #MyTable;

查询的结果是1,3

〜干杯


4

最后,我得出的结论是,如果不修改查询的工作方式,就无法将值存储在变量中。我使用SQL事件探查器捕获值,然后将它们硬编码到查询中以查看其工作方式。这些整数数组有18个,其中一些包含30个以上的元素。

我认为MS / SQL有必要在语言中引入一些附加的数据类型。数组很常见,我不明白为什么不能在存储过程中使用它们。


6
当SQL Server具有表值的参数和变量时,它不需要数组。
约翰·桑德斯

1
所以我们知道查询使用整数列表(由数组传递给它)。我不明白的是您的查询如何使用它们而不使用答案中给定的方法之一。提供更多背景信息,我们将为您提供进一步的帮助。
Möoz

2

您不能这样做,但是可以执行将其存储在变量中的整个查询。

例如:

DECLARE @listOfIDs NVARCHAR(MAX) = 
    '1,2,3'

DECLARE @query NVARCHAR(MAX) = 
    'Select *
     From TabA
     Where TabA.ID in (' + @listOfIDs + ')'

Exec (@query)

2
如前一评论中所述,取决于@listOfIDs是用户提供的参数,这取决于您实施此类解决方案的方式,这很容易受到SQL注入的攻击。
罗加拉

2

string_split如果您使用字符串列表,则SQL中有一个新函数被调用。参考链接STRING_SPLIT(Transact-SQL)

DECLARE @tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(@tags, ',')
WHERE RTRIM(value) <> '';

您可以通过以下方式传递此查询in

SELECT *
  FROM [dbo].[yourTable]
  WHERE (strval IN (SELECT value FROM STRING_SPLIT(@tags, ',') WHERE RTRIM(value) <> ''))

1
看起来很合理,但不幸的是,仅从SQL Server 2016起。
AnotherFineMess

0

我用这个:

1-在建筑物脚本中声明一个临时表变量:

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2-分配给临时表:

IF (SOME CONDITION) 
BEGIN 
        INSERT INTO @ShiftPeriodList SELECT ShiftId FROM [hr].[tbl_WorkShift]
END
IF (SOME CONDITION2)
BEGIN
    INSERT INTO @ShiftPeriodList
        SELECT  ws.ShiftId
        FROM [hr].[tbl_WorkShift] ws
        WHERE ws.WorkShift = 'Weekend(VSD)' OR ws.WorkShift = 'Weekend(SDL)'

END

3-在WHERE语句中需要时参考表:

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
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.