C#SQL Server-ストアドプロシージャにリストを渡す


111

C#コードからSQL Serverストアドプロシージャを呼び出しています。

using (SqlConnection conn = new SqlConnection(connstring))
{
   conn.Open();
   using (SqlCommand cmd = new SqlCommand("InsertQuerySPROC", conn))
   {
      cmd.CommandType = CommandType.StoredProcedure;

      var STableParameter = cmd.Parameters.AddWithValue("@QueryTable", QueryTable);
      var NDistanceParameter = cmd.Parameters.AddWithValue("@NDistanceThreshold", NDistanceThreshold);
      var RDistanceParameter = cmd.Parameters.AddWithValue(@"RDistanceThreshold", RDistanceThreshold);

      STableParameter .SqlDbType = SqlDbType.Structured;
      NDistanceParameter.SqlDbType = SqlDbType.Int;
      RDistanceParameter.SqlDbType = SqlDbType.Int;

      // Execute the query
      SqlDataReader QueryReader = cmd.ExecuteReader();

私のストアドプロシージャはかなり標準的ですが、結合を行いますQueryTable(そのため、ストアドプロシージャを使用する必要があります)。

今:文字列のリストをList<string>パラメーターセットに追加します。たとえば、ストアドプロシージャクエリは次のようになります。

SELECT feature 
FROM table1 t1 
INNER JOIN @QueryTable t2 ON t1.fid = t2.fid 
WHERE title IN <LIST_OF_STRINGS_GOES_HERE>

ただし、文字列のリストは動的で数百に及びます。

文字列のリストをList<string>ストアドプロシージャに渡す方法はありますか?またはこれを行うより良い方法はありますか?

ありがとう、ブレット



どのバージョンのSQL Server?2005 ?? 2008 ?? 2008 R2 ?? SQL Server 2008以降には、「テーブル値パラメーター」という概念があります(詳細については、Redthの応答を参照してください)
marc_s

関連のないヒント:SqlDataReaderもIDisposableなので、usingブロック内に置く必要があります。
Richardissimo 2018年

回答:


178

SQL Server 2008を使用している場合、ユーザー定義のテーブルタイプと呼ばれる新機能があります。これを使用する方法の例を次に示します。

ユーザー定義のテーブルタイプを作成します。

CREATE TYPE [dbo].[StringList] AS TABLE(
    [Item] [NVARCHAR](MAX) NULL
);

次に、ストアドプロシージャで適切に使用する必要があります。

CREATE PROCEDURE [dbo].[sp_UseStringList]
    @list StringList READONLY
AS
BEGIN
    -- Just return the items we passed in
    SELECT l.Item FROM @list l;
END

最後に、c#で使用するsqlを次に示します。

using (var con = new SqlConnection(connstring))
{
    con.Open();

    using (SqlCommand cmd = new SqlCommand("exec sp_UseStringList @list", con))
    {
        using (var table = new DataTable()) {
          table.Columns.Add("Item", typeof(string));

          for (int i = 0; i < 10; i++)
            table.Rows.Add("Item " + i.ToString());

          var pList = new SqlParameter("@list", SqlDbType.Structured);
          pList.TypeName = "dbo.StringList";
          pList.Value = table;

          cmd.Parameters.Add(pList);

          using (var dr = cmd.ExecuteReader())
          {
            while (dr.Read())
                Console.WriteLine(dr["Item"].ToString());
          }
         }
    }
}

SSMSからこれを実行するには

DECLARE @list AS StringList

INSERT INTO @list VALUES ('Apple')
INSERT INTO @list VALUES ('Banana')
INSERT INTO @list VALUES ('Orange')

-- Alternatively, you can populate @list with an INSERT-SELECT
INSERT INTO @list
   SELECT Name FROM Fruits

EXEC sp_UseStringList @list

10
パラメータの値を設定するためにデータテーブルを定義する必要がありますか?他の軽いアプローチはありますか?
ca9163d9 2012

2
試してみましたが、その欠点はEnitiyフレームワークではサポートされていませんでした
Bishoy Hanna

7
LINQ2SQLを使用している場合は、ユーザー定義のテーブルタイプをパラメーターとしてサポートしていないため、このソリューションには注意してください!!! 回避策は、コンマ区切りのリストとパーサー関数を使用したJon Raynorの回答にありますが、これにも欠点があります...
Fazi

3
この場合、@ FaziはLinq2SQLを使用しないでください。T-SQLで文字列の連結と解析を行うことが望ましい
Panagiotis Kanavos

2
ええ、では実際にSSMSからこれをどのように実行しますか?
Sinaesthetic

21

この状況での一般的なパターンは、要素をコンマ区切りのリストで渡し、SQLでそれを使用可能なテーブルに分割することです。ほとんどの人は通常、次のようにこれを行うための指定された関数を作成します。

 INSERT INTO <SomeTempTable>
 SELECT item FROM dbo.SplitCommaString(@myParameter)

そして、それを他のクエリで使用できます。


17
完全を期すために、dbo.SplitCommaString実装のリンクを入れてみましょう:goo.gl/P9ROs
Veli Gebrev '29

3
そして、データフィールドの1つにカンマがあるとどうなりますか?
Kevin Panko 14年

3
代わりにパイプで区切ります。
アレックスインパリ2016年

@AlexInParisデータフィールドの1つにパイプがある場合はどうなりますか?
RayLoveless 2017年

2
次に、データフィールドにないものを使用します。必要に応じてデータをクリーンアップしますが、私が今まで見たデータでパイプを使用したことがない場合。どうしても必要な場合は、¤や§などの他の文字を見つけてください。
Alex In Paris

9

いいえ、配列/リストをSQL Serverに直接渡すことはできません。

次のオプションを使用できます。

  1. コンマで区切られたリストを渡し、SQLで関数を使用すると、リストが分割されます。コンマ区切りのリストは、おそらくNvarchar()として渡されます
  2. xmlを渡し、SQL Serverの関数にリスト内の各値のXMLを解析させる
  3. 新しく定義されたユーザー定義テーブルタイプを使用する(SQL 2008)
  4. SQLを動的に構築し、生のリストを "1,2,3,4"として渡し、SQLステートメントを構築します。これはSQLインジェクション攻撃の傾向がありますが、機能します。

2

はい、ストアドVARCHAR(...) プロシージャパラメーターをasにしてから、コンマ区切りの値をストアドプロシージャに渡します。

SQL Server 2008を使用している場合、TVP(テーブル値パラメーター)を活用できます。QueryTableの構造が文字列の配列よりも複雑な場合はSQL 2008 TVPおよびLINQ、そうでない場合はSQl Server内にテーブルタイプを作成する必要があるため、やり過ぎです


2

Listの代わりに1列のデータテーブルを作成し、文字列をテーブルに追加します。このデータテーブルを構造化タイプとして渡し、テーブルのタイトルフィールドとの別の結合を実行できます。


それが行く方法です。実際にデータベース側にテーブルを作成し、サーバーへのbcp書き込みをロードしました。
18


0

私が知っている唯一の方法は、CSVリストを作成し、それを文字列として渡すことです。次に、SP側で分割して、必要なことをすべて実行します。


0
CREATE TYPE [dbo].[StringList1] AS TABLE(
[Item] [NVARCHAR](MAX) NULL,
[counts][nvarchar](20) NULL);

TYPEをテーブルとして作成し、「StringList1」という名前を付けます

create PROCEDURE [dbo].[sp_UseStringList1]
@list StringList1 READONLY
AS
BEGIN
    -- Just return the items we passed in
    SELECT l.item,l.counts FROM @list l;
    SELECT l.item,l.counts into tempTable FROM @list l;
 End

上記のようにプロシージャを作成し、「UserStringList1」という名前を付けます。

String strConnection = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString.ToString();
            SqlConnection con = new SqlConnection(strConnection);
            con.Open();
            var table = new DataTable();

            table.Columns.Add("Item", typeof(string));
            table.Columns.Add("count", typeof(string));

            for (int i = 0; i < 10; i++)
            {
                table.Rows.Add(i.ToString(), (i+i).ToString());

            }
                SqlCommand cmd = new SqlCommand("exec sp_UseStringList1 @list", con);


                    var pList = new SqlParameter("@list", SqlDbType.Structured);
                    pList.TypeName = "dbo.StringList1";
                    pList.Value = table;

                    cmd.Parameters.Add(pList);
                    string result = string.Empty;
                    string counts = string.Empty;
                    var dr = cmd.ExecuteReader();

                    while (dr.Read())
                    {
                        result += dr["Item"].ToString();
                        counts += dr["counts"].ToString();
                    }

C#で、これを試してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.