将变量传递给SQL函数中的IN子句?

时间:2012-06-28 11:00:52

标签: sql sql-server sql-server-2008 tsql

  

可能重复:
  Parameterizing an SQL IN clause?

我有一个SQL函数,我需要将一个ID列表作为字符串传递到:

WHERE ID IN(@MyList)

我环顾四周,大部分答案都是在C#中构建SQL并且循环并调用AddParameter,或者SQL是动态构建的。

我的SQL函数相当大,因此动态构建查询会相当繁琐。

真的没有办法将一串逗号分隔的值传入IN子句吗?

传入的变量表示整数列表,因此它将是:

“1,2,3,4,5,6,7”等

2 个答案:

答案 0 :(得分:3)

这是一种分割整数列表的更有效方法。首先,如果您还没有数字表,请创建一个数字表。这将创建一个包含100,000个唯一整数的表(您可能需要更多或更少):

;WITH x AS
(
   SELECT TOP (1000000) Number = ROW_NUMBER() OVER 
   (ORDER BY s1.[object_id])
   FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
   ORDER BY s1.[object_id]
)
SELECT Number INTO dbo.Numbers FROM x;

CREATE UNIQUE CLUSTERED INDEX n ON dbo.Numbers(Number);

然后是一个函数:

CREATE FUNCTION [dbo].[SplitInts_Numbers]
(
   @List       NVARCHAR(MAX),
   @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN
   (
       SELECT Item = CONVERT(INT, SUBSTRING(@List, Number,
         CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number))
       FROM dbo.Numbers
       WHERE Number <= CONVERT(INT, LEN(@List))
         AND SUBSTRING(@Delimiter + @List, Number, 1) = @Delimiter
   );

您可以在此处将性能与迭代方法进行比较:

http://sqlfiddle.com/#!3/960d2/1

要避免使用数字表,您还可以尝试基于XML的函数版本 - 它更紧凑但效率更低:

CREATE FUNCTION [dbo].[SplitInts_XML]
(
   @List       VARCHAR(MAX),
   @Delimiter  CHAR(1)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN ( SELECT Item = CONVERT(INT, Item) FROM ( 
     SELECT Item = x.i.value('(./text())[1]', 'int') FROM ( 
       SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(@List, @Delimiter, '</i><i>') 
       + '</i>').query('.') ) AS a CROSS APPLY [XML].nodes('i') AS x(i)) AS y
     WHERE Item IS NOT NULL
   );

无论如何,一旦你有了一个功能,你可以简单地说:

WHERE ID IN (SELECT Item FROM dbo.SplitInts_Numbers(@MyList, ','));

答案 1 :(得分:2)

无法将字符串直接传递到IN子句中。但是,例如,如果要将列表作为字符串提供给存储过程,则可以使用以下脏方法

首先,创建此功能:

CREATE FUNCTION [dbo].[fnNTextToIntTable] (@Data NTEXT)
RETURNS 
    @IntTable TABLE ([Value] INT NULL)
AS
BEGIN
    DECLARE @Ptr int, @Length int, @v nchar, @vv nvarchar(10)

    SELECT @Length = (DATALENGTH(@Data) / 2) + 1, @Ptr = 1

    WHILE (@Ptr < @Length)
    BEGIN
        SET @v = SUBSTRING(@Data, @Ptr, 1)

        IF @v = ','
        BEGIN
            INSERT INTO @IntTable (Value) VALUES (CAST(@vv AS int))
            SET @vv = NULL
        END
        ELSE
        BEGIN
            SET @vv = ISNULL(@vv, '') + @v
        END

        SET @Ptr = @Ptr + 1
    END

    -- If the last number was not followed by a comma, add it to the result set
    IF @vv IS NOT NULL
        INSERT INTO @IntTable (Value) VALUES (CAST(@vv AS int))

    RETURN
END

(注意:这不是我的原始代码,但是由于我工作地点的版本控制系统,我丢失了链接到源代码的标题注释。)

然后像这样使用它:

SELECT  *
FROM    tblMyTable
        INNER JOIN fnNTextToIntTable(@MyList) AS List ON tblMyTable.ID = List.Value

或者,如你的问题:

SELECT  *
FROM    tblMyTable
WHERE   ID IN ( SELECT Value FROM fnNTextToIntTable(@MyList) )