3

SQL 2005 での SQL インジェクションを防ぐために sp_executesql を使用しようとしています。次のような単純なクエリがあります。

SELECT * from table WHERE RegionCode in ('X101', 'B202')

ただし、sp_executesql を使用して以下を実行すると、何も返されません。

Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (@P1)'
SET @ParamDefinition = N'@P1 varchar(100)';
DECLARE @Code as nvarchar(100);
SET @Code = 'X101,B202'
EXECUTE sp_executesql @Cmd, @ParamDefinition, @P1 = @Code

これは私がテストしたものです:

SET @Code = 'X101'   <-- This works, it returns a single region
SET @Code = 'X101,B202'   <--- Returns nothing
SET @Code = '''X101'',''B202'''  <-- Returns nothing

助けてください....私は何を間違えましたか?

4

3 に答える 3

3

これが機能しない理由は、@P1 が 1 つの単一の値として扱われるためです。

たとえば、@Code が X101,B202 の場合、クエリは次のように実行されます。一重引用符を含めた場合でも、RegionCode で検索する値にはそれらの一重引用符が含まれていることが期待されます。

考えているように機能させるには、実際に @Code 変数を @Cmd sql コマンド テキストに連結する必要があります。

SET @Code = '''X101'',''B202'''
SET @Cmd = 'SELECT * FROM Table WHERE RegionCode IN (' + @Code + ')'
EXECUTE (@Cmd)

明らかに、これは SQL インジェクションの可能性を広げるだけなので、このアプローチを使用してそれを確実に防止する場合は、十分に注意する必要があります。

検索する値の動的リストを渡す場合、この状況に対処する別の方法があります。

SQL Server 2005 で使用できる 2 つのアプローチについては、私のブログの例を参照してください。このサイトを簡単にグーグルまたは検索すると、このアプローチについて多くの言及があります)。分割したら、その TABLE 変数をメイン クエリに結合します。2 番目の方法は、値を含む XML BLOB を渡し、SQL Server の組み込み XML 機能を使用することです。これらのアプローチは両方とも、そのリンクのパフォーマンス メトリックで実証されており、動的 SQL は必要ありません。

SQL Server 2008 を使用している場合は、テーブル値パラメーターが最適です。これは、そのリンクで示す 3 番目のアプローチであり、最も効果的です。

于 2010-04-08T14:11:49.407 に答える
2

SQL Server で文字列を分割する方法は多数あります。この記事では、ほぼすべての方法の長所と短所について説明します。

Erland Sommarskog による「SQL Server 2005 以降の配列とリスト、テーブル値パラメーターがそれをカットしない場合」

分割関数を作成する必要があります。これは、分割機能を使用する方法です。

SELECT
    *
    FROM YourTable                               y
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value

私は、TSQL で文字列を分割する数値テーブル アプローチを好みますが、SQL Server で文字列を分割する方法は多数あります。それぞれの長所と短所を説明している前のリンクを参照してください。

NumbersNumbers Table メソッドを機能させるには、1 から 10,000 までの行を含むテーブルを作成する、この 1 回限りのテーブル設定を行う必要があります。

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

Numbers テーブルを設定したら、次の分割関数を作成します。

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''

);
GO 

CSV 文字列を簡単にテーブルに分割して結合したり、動的 SQL 内からでも必要に応じて使用したりできるようになりました。質問の動的パラメーター化クエリで使用する方法は次のとおりです。

DECLARE @Cmd as nvarchar(1000),@ParamDefinition nvarchar(1000);
Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (SELECT ListValue FROM dbo.FN_ListToTable('','',@P1))'
SET @ParamDefinition = N'@P1 varchar(100)';
DECLARE @Code as nvarchar(1000);
SET @Code = 'X101,B202'
EXECUTE sp_executesql @Cmd, @ParamDefinition, @P1 = @Code

試してみる作業サンプルを次に示します (最初に数値テーブルと分割関数をセットアップする必要があります)。

CREATE TABLE YourTable (PK int primary key, RowValue varchar(5))
INSERT YourTable VALUES (1,'A')
INSERT YourTable VALUES (2,'BB')
INSERT YourTable VALUES (3,'CCC')
INSERT YourTable VALUES (4,'DDDD')
INSERT YourTable VALUES (5,'EEE')
INSERT YourTable VALUES (6,'FF')
INSERT YourTable VALUES (7,'G')

DECLARE @SQL              nvarchar(1000)
       ,@ParamDefinition  nvarchar(1000)
       ,@ParamValue       varchar(100)
SELECT @SQL = N'SELECT * FROM YourTable WHERE PK IN (SELECT ListValue FROM dbo.FN_ListToTable('','',@P1))'
      ,@ParamDefinition = N'@P1 varchar(100)'
      ,@ParamValue = '2,4,,,6,,8,,2,,4'
EXECUTE sp_executesql @SQL, @ParamDefinition, @P1 = @ParamValue

出力:

PK          RowValue
----------- --------
2           BB
4           DDDD
6           FF

(3 row(s) affected)
于 2010-04-08T16:55:53.000 に答える
1

問題は単一のパラメーターにあるようです。実際には、次のようになります。

SELECT * from table WHERE RegionCode in ('X101,B202')

また

SELECT * from table WHERE RegionCode in ('''X101'', ''B202''')

つまり、RegionCode が機能するには、'X101,B202'または''X101','B202''(完全な文字列) と等しい必要があります。

ここで 2 つのパラメーターを使用することをお勧めします。

Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (@P1,@P2)'
SET @Code1 = 'X101'
SET @Code2 = 'B202'

ただし、そのリストに 3 つ以上の項目を含める場合は、おそらく一時テーブルまたはテーブル値パラメーターを使用して、別のルートに進むことをお勧めします。

于 2010-04-08T14:14:16.753 に答える