1

重複の可能性:
SQL IN 句をパラメータ化しますか?

IDのリストを文字列として渡す必要があるSQL関数があります。

WHERE ID IN (@MyList)

私は周りを見回しましたが、ほとんどの答えは、SQLがC#内に構築され、それらがループして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 に答える