web-dev-qa-db-ja.com

結果を制限するために、ストアドプロシージャにアイテムのリストをどのように送信しますか?

重複の可能性:
配列パラメーターをストアドプロシージャに渡す

SQLステートメントが次のようになるように、uniqueidentifiersのリストで結果をフィルタリングするとします。

WHERE CustomerID in (@CustomerIDs)

これは可能ですか?

編集:

Nickの質問を明確にして回答するために、値のリストをC#からSQL Serverに渡し、上記で投稿したSQLと同様の選択を実行できるようにしたいと思います。これは、膨大な量のデータがあるため、必要以上のデータを取得して検索するよりも望ましい方法です。

8
coder

SQL 2008以降を使用している場合は、基本的に.NET側からの配列を渡すことができるテーブル値パラメーターを使用できます。ニックが言ったin句としてテーブル変数を使用します。

SQL 2008以降を使用していない場合は、値をXMLドキュメントとして渡し、xqueryまたはOPENXMLを使用してXMLドキュメントを解析し、値を一時テーブル(または方法に応じてテーブル変数)に入れます。あなたが期待する多くの値)。次に、一時テーブルをWHERE句内のINの一部として使用します。

8
mrdenny

SQL2008以前の代替アプローチは、CSVスプリッターを使用することです。 Jeff Modenは、これに対するさまざまなアプローチの 徹底的なテスト を実行しました。

あなたの例は次のようになります:

DECLARE @CustomerIdList VARCHAR(8000)
SET @CustomerIdList = 
                    'F2D13066-111F-4B85-B85B-2C89F4876D06
                    ,DEF52A31-3272-4296-9C2B-3B3D7DB45BBA
                    ,981BCCF6-D1D2-4F19-B5CF-B4CC4FC6B417'

SELECT
    x, y, z
FROM
    dbo.Customer c
INNER JOIN
    dbo.DelimitedSplit8K(@CustomerIdList, ',') IdList
ON  IdList.Item = c.CustomerId

DelimitedSplit8K関数:

CREATE FUNCTION [dbo].[DelimitedSplit8K]
        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), 
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), 
 cteTally(N) AS (
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                )
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;
5

はい、ただし次のように記述します。

_...
WHERE CustomerID IN (
   SELECT CustomerID
   FROM @table_variable
)
_

明らかに、IN (...)の内部を適切なタイプの単一の列を返す任意のクエリに置き換えることができます。これは、フィルタリングしているデータがすでにデータベースのどこかにあると想定しています。このデータを(更新ごとに)ストアドプロシージャに渡す方法については、ここで他の回答を参照します。

このアプローチは、SQL Serverのすべてのバージョンで機能します。 (SQL Server 2005+が必要になるのは、テーブル変数を使用する場合のみです。)

2
Nick Chammas

プロシージャを呼び出す前に一時テーブルを作成し、ストアドプロシージャ内から一時テーブルを参照できます。プロセスからのデータベース接続が開いている限り、一時テーブルは保持されます。

接続プーリングが問題を引き起こしている場合は、グローバル一時テーブル(## MyTableなど)を作成してみてください。これに関する警告は、テーブルが他のすべてのプロセスで使用できることです。そのため、複数のプロセスが同時に使用する場合は、名前を接続に固有にする必要があります。

あるいは、perma-tempテーブルを使用することもできます。これは実際のテーブルですが、プロセスは使用するたびにテーブルを空にします。繰り返しますが、そのテーブルはプロセスの他のインスタンスから見えるため、データに適切なタグを付ける必要がある場合があります。

2
datagod

SQL 2005では、パラメーターをXML文字列として渡すことができます。

これは本当に簡単な方法で完璧に動作します。

DECLARE @productIds xmlSET @productIds ='<Products><id>3</id><id>6</id><id>15</id></Products>'

SELECT
    ParamValues.ID.value('.','VARCHAR(20)') 
FROM 
    @productIds.nodes('/Products/id') as ParamValues(ID) 

これにより、次の3つの行が得られます。


6
15

詳細については、以下を参照してください。

http://weblogs.asp.net/jgalloway/archive/2007/02/16/passing-lists-to-sql-server-2005-with-xml-parameters.aspx

1
geofili

クライアントライブラリからテーブル値の変数をsprocsに取得することは、私が最後に調べたときは不可能でしたが、それはかなり前のことであり、変更された可能性があります。文字列を取得して分割するスプリッターを記述し、リスト内の各項目をテーブル変数に追加できます。次に、クエリしているものに対してテーブル変数を結合します。