338

配列をSQL Serverストアドプロシージャに渡す方法は?

たとえば、従業員のリストがあります。このリストをテーブルとして使用し、別のテーブルと結合したいと考えています。ただし、従業員のリストは C# からパラメーターとして渡す必要があります。

4

13 に答える 13

485

SQL Server 2008 (またはそれ以降)

まず、データベースに次の 2 つのオブジェクトを作成します。

CREATE TYPE dbo.IDList
AS TABLE
(
  ID INT
);
GO

CREATE PROCEDURE dbo.DoSomethingWithEmployees
  @List AS dbo.IDList READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT ID FROM @List; 
END
GO

C# コードで次のようにします。

// Obtain your list of ids to send, this is just an example call to a helper utility function
int[] employeeIds = GetEmployeeIds();

DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("ID", typeof(int)));

// populate DataTable from your List here
foreach(var id in employeeIds)
    tvp.Rows.Add(id);

using (conn)
{
    SqlCommand cmd = new SqlCommand("dbo.DoSomethingWithEmployees", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter tvparam = cmd.Parameters.AddWithValue("@List", tvp);
    // these next lines are important to map the C# DataTable object to the correct SQL User Defined Type
    tvparam.SqlDbType = SqlDbType.Structured;
    tvparam.TypeName = "dbo.IDList";
    // execute query, consume results, etc. here
}

SQL Server 2005

SQL Server 2005 を使用している場合でも、XML より分割関数を使用することをお勧めします。まず、関数を作成します。

CREATE FUNCTION dbo.SplitInts
(
   @List      VARCHAR(MAX),
   @Delimiter VARCHAR(255)
)
RETURNS TABLE
AS
  RETURN ( SELECT Item = CONVERT(INT, Item) FROM
      ( SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')
        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
  );
GO

これで、ストアド プロシージャは次のようになります。

CREATE PROCEDURE dbo.DoSomethingWithEmployees
  @List VARCHAR(MAX)
AS
BEGIN
  SET NOCOUNT ON;

  SELECT EmployeeID = Item FROM dbo.SplitInts(@List, ','); 
END
GO

C# コードでは、リストを次のように渡すだけ'1,2,3,12'です。


テーブル値パラメーターを渡す方法は、それを使用するソリューションの保守性を簡素化し、多くの場合、XML や文字列分割などの他の実装と比較してパフォーマンスが向上します。

入力は明確に定義されており (区切り文字がコンマかセミコロンかを推測する必要はありません)、ストアド プロシージャのコードを検査しないと明らかでない他の処理関数への依存はありません。

UDT の代わりにユーザー定義の XML スキーマを使用するソリューションと比較すると、これには同様の数の手順が含まれますが、私の経験では、管理、保守、および読み取りがはるかに簡単なコードです。

多くのソリューションでは、多くのストアド プロシージャで再利用するこれらの UDT (ユーザー定義型) の 1 つまたはいくつかだけが必要な場合があります。この例のように、一般的な要件は ID ポインターのリストを渡すことです。関数名は、それらの ID が表すコンテキストを記述し、型名はジェネリックにする必要があります。

于 2012-06-19T16:35:39.167 に答える
62

私の経験に基づくと、employeeID から区切られた式を作成することで、この問題に対するトリッキーで優れた解決策があります。';123;434;365;'in-which123のような文字列式のみを作成し、一部の従業員 ID を作成する必要があり434ます365。以下のプロシージャを呼び出してこの式を渡すことで、目的のレコードを取得できます。「別のテーブル」をこのクエリに簡単に結合できます。このソリューションは、SQL サーバーのすべてのバージョンに適しています。また、テーブル変数や一時テーブルを使用する場合と比較して、非常に高速で最適化されたソリューションです。

CREATE PROCEDURE dbo.DoSomethingOnSomeEmployees  @List AS varchar(max)
AS
BEGIN
  SELECT EmployeeID 
  FROM EmployeesTable
  -- inner join AnotherTable on ...
  where @List like '%;'+cast(employeeID as varchar(20))+';%'
END
GO
于 2014-12-02T15:16:24.850 に答える
28

ストアド プロシージャにテーブル値パラメーターを使用します。

C# から渡すときは、データ型が SqlDb.Structured のパラメーターを追加します。

ここを参照してください: http://msdn.microsoft.com/en-us/library/bb675163.aspx

例:

// Assumes connection is an open SqlConnection object.
using (connection)
{
// Create a DataTable with the modified rows.
DataTable addedCategories =
  CategoriesDataTable.GetChanges(DataRowState.Added);

// Configure the SqlCommand and SqlParameter.
SqlCommand insertCommand = new SqlCommand(
    "usp_InsertCategories", connection);
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue(
    "@tvpNewCategories", addedCategories);
tvpParam.SqlDbType = SqlDbType.Structured;

// Execute the command.
insertCommand.ExecuteNonQuery();
}
于 2012-06-19T13:53:17.790 に答える
18

XML パラメータとして渡す必要があります。

編集:あなたにアイデアを与えるために私のプロジェクトからの簡単なコード:

CREATE PROCEDURE [dbo].[GetArrivalsReport]
    @DateTimeFrom AS DATETIME,
    @DateTimeTo AS DATETIME,
    @HostIds AS XML(xsdArrayOfULong)
AS
BEGIN
    DECLARE @hosts TABLE (HostId BIGINT)

    INSERT INTO @hosts
        SELECT arrayOfUlong.HostId.value('.','bigint') data
        FROM @HostIds.nodes('/arrayOfUlong/u') as arrayOfUlong(HostId)

次に、一時テーブルを使用してテーブルと結合できます。データの整合性を維持するために、組み込みの XML スキーマとして arrayOfUlong を定義しましたが、その必要はありません。これを使用することをお勧めしますので、常に long を含む XML を取得するための簡単なコードを次に示します。

IF NOT EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'xsdArrayOfULong')
BEGIN
    CREATE XML SCHEMA COLLECTION [dbo].[xsdArrayOfULong]
    AS N'<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="arrayOfUlong">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded"
                            name="u"
                            type="xs:unsignedLong" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>';
END
GO
于 2012-06-19T13:49:37.750 に答える
14

配列のサイズ複雑さなど、コンテキストは常に重要です。小規模から中規模のリストの場合、ここに投稿された回答のいくつかは問題ありませんが、いくつか明確にする必要があります。

  • 区切りリストを分割するには、SQLCLR ベースのスプリッターが最も高速です。独自の関数を作成したい場合や、CLR 関数の無料のSQL#ライブラリをダウンロードするだけでも、数多くの例があります (これは私が作成しましたが、String_Split 関数やその他の多くの関数は完全に無料です)。
  • XMLベースの配列の分割高速ですが、要素ベースのXMLではなく、属性ベースのXMLを使用する必要があります(ここでの回答に示されている唯一のタイプですが、@AaronBertrandのXMLの例は、彼のコードがtext()XML 関数. XML を使用してリストを分割する方法の詳細 (つまり、パフォーマンス分析) については、Phil Factor による「Using XML to pass lists as parameters in SQL Server」を参照してください。
  • データが proc にストリーミングされ、テーブル変数として事前に解析され、厳密に型指定されて表示されるため、TVP の使用は優れています (少なくとも SQL Server 2008 以降を使用していると仮定します)。ただし、ほとんどの場合、すべてのデータを に格納するDataTableということは、元のコレクションからコピーされたデータをメモリに複製することを意味します。したがってDataTable、TVP を渡す方法を使用しても、より大きなデータ セットにはうまく機能しません (つまり、うまくスケーリングできません)。
  • XML は、Int または String の単純な区切りリストとは異なり、TVP と同様に 1 次元以上の配列を処理できます。しかし、DataTableTVP 方式と同様に、XML ドキュメントのオーバーヘッドを追加で考慮する必要があるため、メモリ内のデータサイズが 2 倍以上になるため、XML はうまく拡張できません。

以上のことから、使用しているデータが大きい場合、またはまだそれほど大きくないが一貫して成長している場合IEnumerable、データを SQL Server にストリーミングするため (メソッドのように)、TVP メソッドが最適な選択ですが、DataTableそうではありません。メモリ内のコレクションの複製が必要です (他の方法とは異なります)。この回答に SQL および C# コードの例を投稿しました。

ディクショナリをストアド プロシージャ T-SQL に渡す

于 2014-09-13T22:00:16.557 に答える
6

SQL Serverでは配列はサポートされていませんが、ストアドプロシージャにコレクションを渡す方法はいくつかあります。

  1. datatableを使用する
  2. XMLを使用する。コレクションをxml形式に変換してから、それを入力としてストアドプロシージャに渡します。

以下のリンクはあなたを助けるかもしれません

コレクションをストアドプロシージャに渡す

于 2012-06-19T13:54:52.273 に答える
6

これはあなたを助けるでしょう。:) 次の手順に従います。

  1. クエリ エディターを開く

  2. 以下のコードをそのままコピペすると、文字列を整数に変換する関数が作成されます

    CREATE FUNCTION dbo.SplitInts
    (
       @List      VARCHAR(MAX),
       @Delimiter VARCHAR(255)
    )
    RETURNS TABLE
    AS
      RETURN ( SELECT Item = CONVERT(INT, Item) FROM
          ( SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')
            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
      );
    GO
    
  3. 次のストアド プロシージャを作成します。

     CREATE PROCEDURE dbo.sp_DeleteMultipleId
     @List VARCHAR(MAX)
     AS
     BEGIN
          SET NOCOUNT ON;
          DELETE FROM TableName WHERE Id IN( SELECT Id = Item FROM dbo.SplitInts(@List, ',')); 
     END
     GO
    
  4. Execute this SP これを使用しexec sp_DeleteId '1,2,3,12'て、削除したい ID の文字列を指定します。

  5. C# で配列を文字列に変換し、以下のようにストアド プロシージャのパラメーターとして渡すことができます。

    int[] intarray = { 1, 2, 3, 4, 5 };  
    string[] result = intarray.Select(x=>x.ToString()).ToArray();
    

     

    SqlCommand command = new SqlCommand();
    command.Connection = connection;
    command.CommandText = "sp_DeleteMultipleId";
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add("@Id",SqlDbType.VARCHAR).Value=result ;
    

これにより、1 回のストアド プロシージャ呼び出しで複数の行が削除されます。ではごきげんよう。

于 2014-12-20T11:46:05.070 に答える
5

このリンクを見つけるまで、新しいテーブル型を作成する手間をかけずに配列をSQLサーバーに渡す方法のすべての例と回答を検索してきました。以下は、プロジェクトに適用した方法です。

-- 次のコードは、配列をパラメータとして取得し、その --array の値を別のテーブルに挿入します。

Create Procedure Proc1 


@UserId int, //just an Id param
@s nvarchar(max)  //this is the array your going to pass from C# code to your Sproc

AS

    declare @xml xml

    set @xml = N'<root><r>' + replace(@s,',','</r><r>') + '</r></root>'

    Insert into UserRole (UserID,RoleID)
    select 
       @UserId [UserId], t.value('.','varchar(max)') as [RoleId]


    from @xml.nodes('//root/r') as a(t)
END 

楽しんでくれると良いです

于 2014-07-17T16:34:16.907 に答える
0

SQL Server 2016 以降では、リストを NVARCHAR() として取り込み、OPENJSON を使用できます

DECLARE @EmployeeList nvarchar(500) = '[1,2,15]'


SELECT * 
FROM Employees
WHERE ID IN (SELECT VALUE FROM OPENJSON(@EmployeeList ))
于 2021-06-15T17:03:38.767 に答える