1

可能重复:
参数化 SQL IN 子句?

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

身份在哪里(@MyList)

我环顾四周,大多数答案要么是在 C# 中构建 SQL 并且它们循环并调用 AddParameter,要么是动态构建 SQL。

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

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

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

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

4

2 回答 2

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, ','));
于 2012-06-28T13:58:53.847 回答
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) )
于 2012-06-28T11:12:09.613 回答