複数のID値を受け入れるT-SQLストアドプロシージャ


145

IDのリストをパラメーターとしてストアード・プロシージャーに渡すことを処理する適切な方法はありますか?

たとえば、ストアドプロシージャによって部門1、2、5、7、20が返されるようにしたいとします。過去に、以下のコードのように、コンマで区切られたIDのリストを渡しましたが、それを行うのは本当に汚い感じがします。

SQL Server 2005は私の唯一の適用可能な制限だと思います。

create procedure getDepartments
  @DepartmentIds varchar(max)
as
  declare @Sql varchar(max)     
  select @Sql = 'select [Name] from Department where DepartmentId in (' + @DepartmentIds + ')'
  exec(@Sql)

ここに私が見つけたXMLメソッドの変形があります。
JasonS 2008

5
SQL Server 2008を使用している場合は、テーブル値パラメーターを使用できます。http://www.sqlteam.com/article/sql-server-2008-table-valued-parameters
Ian Nelson

これは私にとって役に立ちました:sqlmag.com/t-sql/passing-multivalued-variables-stored-procedure
Zameer

回答:


237

Erland Sommarskogは、この質問に対する権威ある回答を過去16年間維持していますSQL Serverの配列とリスト

クエリに配列またはリストを渡す方法は少なくとも12通りあります。それぞれに独自の長所と短所があります。

  • テーブル値パラメーター。SQL Server 2008以降のみで、おそらく最も一般的な「最良の」アプローチに最も近い。
  • 反復法。区切られた文字列を渡してループします。
  • CLRの使用。SQL Server 2005以降(.NET言語のみ)。
  • XML。多くの行を挿入するのに非常に適しています。SELECTにとってはやり過ぎかもしれません。
  • 数字の表。単純な反復法よりも高いパフォーマンス/複雑さ。
  • 固定長要素。固定長により、区切られた文字列よりも速度が向上します
  • 数字の機能。テーブルから取得するのではなく、関数で生成される数のテーブルと固定長のバリエーション。
  • 再帰的共通テーブル式(CTE)。SQL Server 2005以降、繰り返し方式よりも複雑すぎず、パフォーマンスも優れています。
  • 動的SQL。遅くなる可能性があり、セキュリティに影響します。
  • 多くのパラメータとしてリストを渡す。面倒でエラーが発生しやすいですが、シンプルです。
  • 本当に遅いメソッド。charindex、patindexまたはLIKEを使用するメソッド。

これらのすべてのオプション間のトレードオフについて学ぶために記事読むのに十分にお勧めすることはできません。


11

ええ、現在のソリューションはSQLインジェクション攻撃を受けやすいです。

私が見つけた最良の解決策は、テキストを単語に分割する関数を使用して(ここにいくつか投稿されているか、または私のブログからこれを使用できます)、それをテーブルに結合することです。何かのようなもの:

SELECT d.[Name]
FROM Department d
    JOIN dbo.SplitWords(@DepartmentIds) w ON w.Value = d.DepartmentId

14
ストアドプロシージャが信頼できないクライアントから直接呼び出すことができない場合を除いて、 "SQLインジェクション攻撃が発生しやすい"かどうかはわかりません。この場合、大きな問題が発生します。サービス層コードは、厳密に型指定されたデータ(int [] departmentIdsなど)から@DepartmentIds文字列を生成する必要があります。その場合は問題ありません。
Anthony、

素晴らしいソリューション、@マットハミルトン。これが誰にも役立つかどうかはわかりませんが、「join dbo.SplitWords(@MyParameterArray)p ON CHARINDEX(p.value、d.MyFieldToSearch)> 0」を使用してテキストフィールドを検索すると、SQL Server 2008rでより正確な結果が得られました
Darkloki

3

値を頻繁に処理する場合に検討する方法の1つは、最初に一時テーブルに値を書き込むことです。次に、通常どおりに参加します。

この方法では、解析は1回だけです。

「分割」UDFの1つを使用するのが最も簡単ですが、多くの人々がそれらの例を投稿しているので、別のルートに行くと思いました;)

この例では、(#tmpDept)に参加するための一時テーブルを作成し、それに渡した部門IDを入力します。コンマで区切ることを想定していますが、もちろん、変更することができます。好きなように。

IF OBJECT_ID('tempdb..#tmpDept', 'U') IS NOT NULL
BEGIN
    DROP TABLE #tmpDept
END

SET @DepartmentIDs=REPLACE(@DepartmentIDs,' ','')

CREATE TABLE #tmpDept (DeptID INT)
DECLARE @DeptID INT
IF IsNumeric(@DepartmentIDs)=1
BEGIN
    SET @DeptID=@DepartmentIDs
    INSERT INTO #tmpDept (DeptID) SELECT @DeptID
END
ELSE
BEGIN
        WHILE CHARINDEX(',',@DepartmentIDs)>0
        BEGIN
            SET @DeptID=LEFT(@DepartmentIDs,CHARINDEX(',',@DepartmentIDs)-1)
            SET @DepartmentIDs=RIGHT(@DepartmentIDs,LEN(@DepartmentIDs)-CHARINDEX(',',@DepartmentIDs))
            INSERT INTO #tmpDept (DeptID) SELECT @DeptID
        END
END

これにより、1つの部門ID、コンマを間に挟んだ複数のID、またはコンマとスペースを挟んだ複数のIDを渡すことができます。

したがって、次のようなことをした場合:

SELECT Dept.Name 
FROM Departments 
JOIN #tmpDept ON Departments.DepartmentID=#tmpDept.DeptID
ORDER BY Dept.Name

渡したすべての部門IDの名前が表示されます...

繰り返しますが、これは一時テーブルにデータを入力する関数を使用することで簡略化できます...私は退屈なものを殺すためだけに主にそれを使わずにそれを行いました:-P

-ケビンフェアチャイルド


3

XMLを使用できます。

例えば

declare @xmlstring as  varchar(100) 
set @xmlstring = '<args><arg value="42" /><arg2>-1</arg2></args>' 

declare @docid int 

exec sp_xml_preparedocument @docid output, @xmlstring

select  [id],parentid,nodetype,localname,[text]
from    openxml(@docid, '/args', 1) 

コマンドsp_xml_preparedocumentが組み込まれています。

これは出力を生成します:

id  parentid    nodetype    localname   text
0   NULL        1           args        NULL
2   0           1           arg         NULL
3   2           2           value       NULL
5   3           3           #text       42
4   0           1           arg2        NULL
6   4           3           #text       -1

必要なものがすべて(もっと?)


2

ストアドプロシージャを使用してコンマで区切られた部門IDのリストを渡す場合は、超高速XMLメソッド:

Declare @XMLList xml
SET @XMLList=cast('<i>'+replace(@DepartmentIDs,',','</i><i>')+'</i>' as xml)
SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))

すべてのクレジットは、グルブラッドシュルツのブログに送られます


-3

これを試してください:

@list_of_params varchar(20) -- value 1, 2, 5, 7, 20 

SELECT d.[Name]
FROM Department d
where @list_of_params like ('%'+ CONVERT(VARCHAR(10),d.Id)  +'%')

とてもシンプル。


1
非常に単純です-非常に間違っています。しかし、コードで問題を修正したとしても、非常に遅くなります。詳細については、承認された回答の「本当に遅いメソッド」リンクを参照してください。
Sebastian Meine
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.