有没有办法参数化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);
更聪明/更安全的方法是什么?
答案 0 :(得分:4)
您可以在循环内添加参数,例如:
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 document approach Table Valued Parameters
来自博客的关于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-Value类型,填充它,然后传递 它在SqlParameter对象中作为SqlDbType.Structured。
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