配列をSQL Serverストアドプロシージャに渡す方法は?
たとえば、従業員のリストがあります。このリストをテーブルとして使用し、別のテーブルと結合したいと考えています。ただし、従業員のリストは C# からパラメーターとして渡す必要があります。
配列をSQL Serverストアドプロシージャに渡す方法は?
たとえば、従業員のリストがあります。このリストをテーブルとして使用し、別のテーブルと結合したいと考えています。ただし、従業員のリストは C# からパラメーターとして渡す必要があります。
まず、データベースに次の 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 を使用している場合でも、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 が表すコンテキストを記述し、型名はジェネリックにする必要があります。
私の経験に基づくと、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
ストアド プロシージャにテーブル値パラメーターを使用します。
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();
}
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
配列のサイズや複雑さなど、コンテキストは常に重要です。小規模から中規模のリストの場合、ここに投稿された回答のいくつかは問題ありませんが、いくつか明確にする必要があります。
text()
XML 関数. XML を使用してリストを分割する方法の詳細 (つまり、パフォーマンス分析) については、Phil Factor による「Using XML to pass lists as parameters in SQL Server」を参照してください。DataTable
ということは、元のコレクションからコピーされたデータをメモリに複製することを意味します。したがってDataTable
、TVP を渡す方法を使用しても、より大きなデータ セットにはうまく機能しません (つまり、うまくスケーリングできません)。DataTable
TVP 方式と同様に、XML ドキュメントのオーバーヘッドを追加で考慮する必要があるため、メモリ内のデータサイズが 2 倍以上になるため、XML はうまく拡張できません。以上のことから、使用しているデータが大きい場合、またはまだそれほど大きくないが一貫して成長している場合IEnumerable
、データを SQL Server にストリーミングするため (メソッドのように)、TVP メソッドが最適な選択ですが、DataTable
そうではありません。メモリ内のコレクションの複製が必要です (他の方法とは異なります)。この回答に SQL および C# コードの例を投稿しました。
SQL Serverでは配列はサポートされていませんが、ストアドプロシージャにコレクションを渡す方法はいくつかあります。
以下のリンクはあなたを助けるかもしれません
これはあなたを助けるでしょう。:) 次の手順に従います。
クエリ エディターを開く
以下のコードをそのままコピペすると、文字列を整数に変換する関数が作成されます
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.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
Execute this SP これを使用しexec sp_DeleteId '1,2,3,12'
て、削除したい ID の文字列を指定します。
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 回のストアド プロシージャ呼び出しで複数の行が削除されます。ではごきげんよう。
このリンクを見つけるまで、新しいテーブル型を作成する手間をかけずに配列を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
楽しんでくれると良いです
SQL Server 2016 以降では、リストを NVARCHAR() として取り込み、OPENJSON を使用できます
DECLARE @EmployeeList nvarchar(500) = '[1,2,15]'
SELECT *
FROM Employees
WHERE ID IN (SELECT VALUE FROM OPENJSON(@EmployeeList ))