146

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)
4

5 に答える 5

240

Erland Sommarskog は、過去 16 年間、この質問に対する信頼できる答えを維持してきました: Arrays and Lists in SQL Server

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

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

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

于 2008-09-04T13:32:34.507 に答える
11

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

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

SELECT d.[Name]
FROM Department d
    JOIN dbo.SplitWords(@DepartmentIds) w ON w.Value = d.DepartmentId
于 2008-09-04T06:55:28.237 に答える
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

必要なものがすべて(もっと?)あります。

于 2008-09-04T07:05:13.957 に答える
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

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

于 2008-09-04T16:36:51.897 に答える
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))

すべてのクレジットはGuruBradSchulzのブログに送られます

于 2013-03-03T22:19:36.663 に答える