web-dev-qa-db-ja.com

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

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>をストアドプロシージャに渡す方法はありますか?または、これを行うより良い方法はありますか?

ありがとう、ブレット

103
Brett

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
165
Redth

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

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

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

20
Tejs

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

次のオプションが利用可能です。

  1. コンマ区切りのリストを渡し、SQLの関数を使用してリストを分割します。コンマ区切りリストは、Nvarchar()として渡される可能性が高いです。
  2. Xmlを渡し、SQL Serverの関数がリスト内の各値のXMLを解析します
  3. 新しく定義されたユーザー定義のテーブルタイプを使用する(SQL 2008)
  4. SQLを動的に構築し、未加工リストを「1,2,3,4」として渡し、SQLステートメントを構築します。これはSQLインジェクション攻撃を受けやすいですが、機能します。
7
Jon Raynor

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

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

2
sll

SQLでCSVリストを分割する場合は、 共通テーブル式 (CTE)を使用して別の方法で分割できます。 CTEを使用して文字列を分割する効率的な方法 を参照してください。

1
Larry Silverman

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

1
hungryMind

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

0
Andrey Agibalov
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#で、これを試してください

0
Deepalakshmi