SQL Server - 带有声明变量的In子句

Mel*_*sus 47 sql in-clause sql-server-2008

假设我得到以下内容:

DECLARE @ExcludedList VARCHAR(MAX)

SET @ExcludedList = 3 + ', ' + 4 + ' ,' + '22'

SELECT * FROM A WHERE Id NOT IN (@ExcludedList)
Run Code Online (Sandbox Code Playgroud)

错误:将varchar值','转换为数据类型int时转换失败.

我理解为什么错误存在,但我不知道如何解决它...

Ton*_*nyP 39

你需要像动态sp一样执行它

DECLARE @ExcludedList VARCHAR(MAX)

SET @ExcludedList = '3,4,22,6014'
declare @sql nvarchar(Max)

Set @sql='SELECT * FROM [A] WHERE Id NOT IN ('+@ExcludedList+')'

exec sp_executesql @sql
Run Code Online (Sandbox Code Playgroud)

  • 如果用户输入ExcludedList,这是否容易受到SQL注入攻击?例如,ExcludedList ='3); DROP TABLE用户; - '(评论系统不会让我使用(at)标志) (9认同)

小智 34

这是一个示例,我使用表变量列出IN子句中的多个值.显而易见的原因是能够在长程序中仅将值列表更改为一个位置.

为了使它更具动态性并允许用户输入,我建议为输入声明一个varchar变量,然后使用WHILE循环传递变量中的数据并将其插入表变量中.

用真实的东西替换@your_list,Your_table和值.

DECLARE @your_list TABLE (list varchar(25)) 
INSERT into @your_list
VALUES ('value1'),('value2376')

SELECT *  
FROM your_table 
WHERE your_column in ( select list from @your_list )
Run Code Online (Sandbox Code Playgroud)

选择语句abowe将执行相同的操作:

SELECT *  
FROM your_table 
WHERE your_column in ('value','value2376' )
Run Code Online (Sandbox Code Playgroud)


小智 20

DECLARE @IDQuery VARCHAR(MAX)
SET @IDQuery = 'SELECT ID FROM SomeTable WHERE Condition=Something'
DECLARE @ExcludedList TABLE(ID VARCHAR(MAX))
INSERT INTO @ExcludedList EXEC(@IDQuery)    
SELECT * FROM A WHERE Id NOT IN (@ExcludedList)
Run Code Online (Sandbox Code Playgroud)

我知道我正在回复一篇旧帖子,但我想分享一个如何在需要避免使用动态SQL时使用变量表的示例.我不确定它是否是最有效的方式,但是当动态SQL不是一个选项时,这对我来说过去了.

  • 我喜欢这个,因为你避免使用动态SQL (4认同)
  • 我以为`EXEC(@IDQuery)`是动态的吗? (4认同)
  • IN(@variable) 需要一个标量,而不是表变量 (3认同)

OMG*_*ies 5

您不能在IN子句中使用变量-您需要使用动态SQL或使用函数(TSQL或CLR)将值列表转换为表

动态SQL示例:

DECLARE @ExcludedList VARCHAR(MAX)
    SET @ExcludedList = 3 + ',' + 4 + ',' + '22'

DECLARE @SQL NVARCHAR(4000)
    SET @SQL = 'SELECT * FROM A WHERE Id NOT IN (@ExcludedList) '

 BEGIN

   EXEC sp_executesql @SQL '@ExcludedList VARCHAR(MAX)' @ExcludedList

 END
Run Code Online (Sandbox Code Playgroud)


lau*_*dly 5

首先,创建一个快速函数,将分隔的值列表拆分为一个表,如下所示:

CREATE FUNCTION dbo.udf_SplitVariable
(
    @List varchar(8000),
    @SplitOn varchar(5) = ','
)

RETURNS @RtnValue TABLE
(
    Id INT IDENTITY(1,1),
    Value VARCHAR(8000)
)

AS
BEGIN

--Account for ticks
SET @List = (REPLACE(@List, '''', ''))

--Account for 'emptynull'
IF LTRIM(RTRIM(@List)) = 'emptynull'
BEGIN
    SET @List = ''
END

--Loop through all of the items in the string and add records for each item
WHILE (CHARINDEX(@SplitOn,@List)>0)
BEGIN

    INSERT INTO @RtnValue (value)
    SELECT Value = LTRIM(RTRIM(SUBSTRING(@List, 1, CHARINDEX(@SplitOn, @List)-1)))  

    SET @List = SUBSTRING(@List, CHARINDEX(@SplitOn,@List) + LEN(@SplitOn), LEN(@List))

END

INSERT INTO @RtnValue (Value)
SELECT Value = LTRIM(RTRIM(@List))

RETURN

END 
Run Code Online (Sandbox Code Playgroud)

然后像这样调用函数......

SELECT * 
FROM A
LEFT OUTER JOIN udf_SplitVariable(@ExcludedList, ',') f ON A.Id = f.Value
WHERE f.Id IS NULL
Run Code Online (Sandbox Code Playgroud)

这对我们的项目非常有效......

当然,如果是这样的话,也可以做相反的事情(虽然不是你的问题).

SELECT * 
FROM A
INNER JOIN udf_SplitVariable(@ExcludedList, ',') f ON A.Id = f.Value
Run Code Online (Sandbox Code Playgroud)

在处理具有可选多选参数列表的报表时,这非常方便.如果参数为NULL,则需要选择所有值,但如果它具有一个或多个值,则希望在这些值上过滤报表数据.然后像这样使用SQL:

SELECT * 
FROM A
INNER JOIN udf_SplitVariable(@ExcludedList, ',') f ON A.Id = f.Value OR @ExcludeList IS NULL
Run Code Online (Sandbox Code Playgroud)

这样,如果@ExcludeList是NULL值,则连接中的OR子句将成为关闭对此值进行过滤的开关.非常便利...