繁体   English   中英

参数化多行的插入

[英]Parameterize insert of multiple rows

有没有办法参数化SQL INSERT语句(在C#中),它插入多行? 目前我只想到一种方法,生成一个插入多行的语句,但这对SQL注入是非常开放的:

string sql = " INSERT INTO  my_table"
           + " (a, b, c)"
           + " VALUES";

// Add each row of values to the statement
foreach (var item in collection) {
    sql = sql
        + String.Format(" ({0}, {1}, {2}),",
              aVal, bVal, cVal);
}

// Remove the excessive comma
sql = sql.Remove(sql.Length - 1);

更聪明/更安全的方法是什么?

您可以在循环内添加参数,例如:

using (var comm = new SqlCommand()) {
        var counter = 0;
         foreach (var item in collection) {
            sql = sql + String.Format(" (@a{0}, @b{0}, @c{0}),"  counter);

            comm.Parameters.AddWithValue("@a" + counter, aVal); 
            comm.Parameters.AddWithValue("@b" + counter, bVal);
            comm.Parameters.AddWithValue("@c" + counter, cVal);
            counter++;
        }
    }

但我真的不会像这样做多行插入。 IIRC查询中的最大参数大约是2100,这可能会非常快。 无论如何,当您循环遍历集合时,您可以将其发送到循环中的数据库,例如:

using (var con = new SqlConnection("connectionString here"))
{
   con.Open();
   var sql = "INSERT INTO  my_table (a, b, c) VALUES (@a,@b,@c);"

   using (var comm = new SqlCommand(sql, con))
   {
       comm.Parameters.Add("@a", SqlDbType.Int);
       comm.Parameters.Add("@b", SqlDbType.NVarChar);
       comm.Parameters.Add("@c", SqlDbType.Int);
       foreach (var item in collection) {
       {
           comm.Parameters["@a"].Value = aVal;
           comm.Parameters["@b"].Value = bVal;
           comm.Parameters["@b"].Size = bVal.Length;
           comm.Parameters["@c"].Value = cVal;

           comm.ExecuteNonQuery();
       }
   }
}

该语句只准备一次(并且比具有100个参数的巨大语句更快),并且当一个记录失败时它不会使所有记录失败(为此添加一些异常处理)。 如果您想在一条记录失败时全部失败,您可以将事物包装在事务中。

编辑:当然,当你经常需要输入1000行时,这种方法也不是最有效的,你的DBA可能会开始抱怨。 此问题还有其他方法可以消除数据库中的压力:例如,在数据库中创建一个将从xml文档插入数据的存储过程,或使用表值参数。 NYCdotNet写了两篇关于这些选项的好博客,我不会在这里重新创建,但是值得探索(我会根据指南在博客下面粘贴一些代码,但应该归功于它:NYCdotNet) XML文档方法 表值参数

来自博客的关于TVP的“肉”(在VB.NET中,但这应该无关紧要):

所以我创建了这个“通用”表值类型:

 CREATE TYPE dbo.UniqueIntegerList AS TABLE

 (

        TheInteger INT NOT NULL

     PRIMARY KEY (TheInteger)

 );

创建保存存储过程

接下来,我创建了一个新的存储过程,它将接受我的新表值类型作为参数。

 CREATE PROC DoTableValuedParameterInsert(@ProductIDs
 dbo.UniqueIntegerList READONLY)

 AS BEGIN



        INSERT INTO ProductsAccess(ProductID)

        SELECT TheInteger AS [ProductID]

        FROM @ProductIDs;



 END

在这个过程中,我传入一个名为@ProductIDs的参数。 这是我刚刚在上一步中创建的“dbo.UniqueIntegerList”类型。 SQL Server看了​​这个并说“哦,我知道这是什么 - 这种类型实际上是一个表”。 因为它知道UniqueIntegerList类型是一个表,所以我可以从中选择,就像我可以从任何其他表值变量中选择一样。 您必须将参数标记为READONLY,因为SQL 2008不支持更新并返回传递的表值参数。

创建保存例程

然后我不得不在我的业务对象上创建一个新的保存例程来调用新的存储过程。 准备Table-Valued参数的方法是创建一个DataTable对象,其具有与Table-Valued类型相同的列签名,填充它,然后将其作为SqlDbType.Structured传递到SqlParameter对象中。

 Public Sub SaveViaTableValuedParameter()



   'Prepare the Table-valued Parameter'

  Dim objUniqueIntegerList As New DataTable

  Dim objColumn As DataColumn =
  objUniqueIntegerList.Columns.Add("TheInteger", _

  System.Type.GetType("System.Int32"))

   objColumn.Unique = True



   'Populate the Table-valued Parameter with the data to save'

   For Each Item As Product In Me.Values

     objUniqueIntegerList.Rows.Add(Item.ProductID)

   Next



   'Connect to the DB and save it.'

   Using objConn As New SqlConnection(DBConnectionString())

     objConn.Open()

     Using objCmd As New SqlCommand("dbo.DoTableValuedParameterInsert")

       objCmd.CommandType = CommandType.StoredProcedure

       objCmd.Connection = objConn

       objCmd.Parameters.Add("ProductIDs", SqlDbType.Structured)



       objCmd.Parameters(0).Value = objUniqueIntegerList



       objCmd.ExecuteNonQuery()

     End Using

     objConn.Close()

   End Using

 End Sub

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM