我该如何多次插入多条记录?

我有一个名为Entry的类,声明如下:

 class Entry{ string Id {get;set;} string Name {get;set;} } 

然后使用ADO.NET接受多个这样的Entry对象插入数据库的方法:

 static void InsertEntries(IEnumerable entries){ //build a SqlCommand object using(SqlCommand cmd = new SqlCommand()){ ... const string refcmdText = "INSERT INTO Entries (id, name) VALUES (@id{0},@name{0});"; int count = 0; string query = string.Empty; //build a large query foreach(var entry in entries){ query += string.Format(refcmdText, count); cmd.Parameters.AddWithValue(string.Format("@id{0}",count), entry.Id); cmd.Parameters.AddWithValue(string.Format("@name{0}",count), entry.Name); count++; } cmd.CommandText=query; //and then execute the command ... } } 

我的问题是:我应该继续使用上面的方式发送多个insert语句(构建一个巨大的insert语句及其参数字符串并通过网络发送),或者我应该保持一个打开的连接并发送一个insert语句对于每个Entry如下所示:

 using(SqlCommand cmd = new SqlCommand(){ using(SqlConnection conn = new SqlConnection(){ //assign connection string and open connection ... cmd.Connection = conn; foreach(var entry in entries){ cmd.CommandText= "INSERT INTO Entries (id, name) VALUES (@id,@name);"; cmd.Parameters.AddWithValue("@id", entry.Id); cmd.Parameters.AddWithValue("@name", entry.Name); cmd.ExecuteNonQuery(); } } } 

你怎么看? 两者之间的Sql Server会有性能差异吗? 我应该注意其他任何后果吗?

如果我是你,我不会使用其中任何一个。

第一个缺点是如果列表中有相同的值,参数名称可能会发生冲突。

第二个缺点是您正在为每个实体创建命令和参数。

最好的方法是将命令文本和参数构造一次(使用Parameters.Add添加参数)在循环中更改它们的值并执行命令。 这样,该声明只准备一次。 您还应该在开始循环之前打开连接并在之后关闭它。

 static void InsertSettings(IEnumerable settings) { using (SqlConnection oConnection = new SqlConnection("Data Source=(local);Initial Catalog=Wip;Integrated Security=True")) { oConnection.Open(); using (SqlTransaction oTransaction = oConnection.BeginTransaction()) { using (SqlCommand oCommand = oConnection.CreateCommand()) { oCommand.Transaction = oTransaction; oCommand.CommandType = CommandType.Text; oCommand.CommandText = "INSERT INTO [Setting] ([Key], [Value]) VALUES (@key, @value);"; oCommand.Parameters.Add(new SqlParameter("@key", SqlDbType.NChar)); oCommand.Parameters.Add(new SqlParameter("@value", SqlDbType.NChar)); try { foreach (var oSetting in settings) { oCommand.Parameters[0].Value = oSetting.Key; oCommand.Parameters[1].Value = oSetting.Value; if (oCommand.ExecuteNonQuery() != 1) { //'handled as needed, //' but this snippet will throw an exception to force a rollback throw new InvalidProgramException(); } } oTransaction.Commit(); } catch (Exception) { oTransaction.Rollback(); throw; } } } } } 

你应该在每个循环上执行命令,而不是构建一个巨大的命令Text(顺便说一句, StringBuilder就是这样做的)底层连接不会关闭并重新打开每个循环,让连接池管理器处理这个。 有关更多信息,请查看此链接: 在ASP.NET应用程序中调整ADO.NET连接池

如果要确保成功执行每个命令,可以根据需要使用事务和回滚,

当它是很多条目时考虑使用SqlBulkCopy 。 性能比一系列单个插件快得多。

跟进@Tim Mahy – 有两种可能的方式来提供SqlBulkCopy:DataReader或通过DataTable。 这里是DataTable的代码:

 DataTable dt = new DataTable(); dt.Columns.Add(new DataColumn("Id", typeof(string))); dt.Columns.Add(new DataColumn("Name", typeof(string))); foreach (Entry entry in entries) dt.Rows.Add(new string[] { entry.Id, entry.Name }); using (SqlBulkCopy bc = new SqlBulkCopy(connection)) { // the following 3 lines might not be neccessary bc.DestinationTableName = "Entries"; bc.ColumnMappings.Add("Id", "Id"); bc.ColumnMappings.Add("Name", "Name"); bc.WriteToServer(dt); } 

如果DataTable正确创建,您可以直接插入它。

首先确保访问表列具有相同的列名和相似的类型。 然后你可以使用这个我相信非常快速和优雅的function。

 public void AccessBulkCopy(DataTable table) { foreach (DataRow r in table.Rows) r.SetAdded(); var myAdapter = new OleDbDataAdapter("SELECT * FROM " + table.TableName, _myAccessConn); var cbr = new OleDbCommandBuilder(myAdapter); cbr.QuotePrefix = "["; cbr.QuoteSuffix = "]"; cbr.GetInsertCommand(true); myAdapter.Update(table); } 

使用单个插入插入多个记录的存储过程:

 ALTER PROCEDURE [dbo].[Ins] @i varchar(50), @n varchar(50), @a varchar(50), @i1 varchar(50), @n1 varchar(50), @a1 varchar(50), @i2 varchar(50), @n2 varchar(50), @a2 varchar(50) AS INSERT INTO t1 SELECT @i AS Expr1, @i1 AS Expr2, @i2 AS Expr3 UNION ALL SELECT @n AS Expr1, @n1 AS Expr2, @n2 AS Expr3 UNION ALL SELECT @a AS Expr1, @a1 AS Expr2, @a2 AS Expr3 RETURN 

代码背后:

 protected void Button1_Click(object sender, EventArgs e) { cn.Open(); SqlCommand cmd = new SqlCommand("Ins",cn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddWithValue("@i",TextBox1.Text); cmd.Parameters.AddWithValue("@n",TextBox2.Text); cmd.Parameters.AddWithValue("@a",TextBox3.Text); cmd.Parameters.AddWithValue("@i1",TextBox4.Text); cmd.Parameters.AddWithValue("@n1",TextBox5.Text); cmd.Parameters.AddWithValue("@a1",TextBox6.Text); cmd.Parameters.AddWithValue("@i2",TextBox7.Text); cmd.Parameters.AddWithValue("@n2",TextBox8.Text); cmd.Parameters.AddWithValue("@a2",TextBox9.Text); cmd.ExecuteNonQuery(); cn.Close(); Response.Write("inserted"); clear(); } 
 ClsConectaBanco bd = new ClsConectaBanco(); StringBuilder sb = new StringBuilder(); sb.Append(" INSERT INTO FAT_BALANCETE "); sb.Append(" ([DT_LANCAMENTO] "); sb.Append(" ,[ID_LANCAMENTO_CONTABIL] "); sb.Append(" ,[NR_DOC_CONTABIL] "); sb.Append(" ,[TP_LANCAMENTO_GERADO] "); sb.Append(" ,[VL_LANCAMENTO] "); sb.Append(" ,[TP_NATUREZA] "); sb.Append(" ,[CD_EMPRESA] "); sb.Append(" ,[CD_FILIAL] "); sb.Append(" ,[CD_CONTA_CONTABIL] "); sb.Append(" ,[DS_CONTA_CONTABIL] "); sb.Append(" ,[ID_CONTA_CONTABIL] "); sb.Append(" ,[DS_TRIMESTRE] "); sb.Append(" ,[DS_SEMESTRE] "); sb.Append(" ,[NR_TRIMESTRE] "); sb.Append(" ,[NR_SEMESTRE] "); sb.Append(" ,[NR_ANO] "); sb.Append(" ,[NR_MES] "); sb.Append(" ,[NM_FILIAL]) "); sb.Append(" VALUES "); sb.Append(" (@DT_LANCAMENTO "); sb.Append(" ,@ID_LANCAMENTO_CONTABIL "); sb.Append(" ,@NR_DOC_CONTABIL "); sb.Append(" ,@TP_LANCAMENTO_GERADO "); sb.Append(" ,@VL_LANCAMENTO "); sb.Append(" ,@TP_NATUREZA "); sb.Append(" ,@CD_EMPRESA "); sb.Append(" ,@CD_FILIAL "); sb.Append(" ,@CD_CONTA_CONTABIL "); sb.Append(" ,@DS_CONTA_CONTABIL "); sb.Append(" ,@ID_CONTA_CONTABIL "); sb.Append(" ,@DS_TRIMESTRE "); sb.Append(" ,@DS_SEMESTRE "); sb.Append(" ,@NR_TRIMESTRE "); sb.Append(" ,@NR_SEMESTRE "); sb.Append(" ,@NR_ANO "); sb.Append(" ,@NR_MES "); sb.Append(" ,@NM_FILIAL) "); SqlCommand cmd = new SqlCommand(sb.ToString(), bd.CriaConexaoSQL()); bd.AbrirConexao(); cmd.Parameters.Add("@DT_LANCAMENTO", SqlDbType.Date); cmd.Parameters.Add("@ID_LANCAMENTO_CONTABIL", SqlDbType.Int); cmd.Parameters.Add("@NR_DOC_CONTABIL", SqlDbType.VarChar,255); cmd.Parameters.Add("@TP_LANCAMENTO_GERADO", SqlDbType.VarChar,255); cmd.Parameters.Add("@VL_LANCAMENTO", SqlDbType.Decimal); cmd.Parameters["@VL_LANCAMENTO"].Precision = 15; cmd.Parameters["@VL_LANCAMENTO"].Scale = 2; cmd.Parameters.Add("@TP_NATUREZA", SqlDbType.VarChar, 1); cmd.Parameters.Add("@CD_EMPRESA",SqlDbType.Int); cmd.Parameters.Add("@CD_FILIAL", SqlDbType.Int); cmd.Parameters.Add("@CD_CONTA_CONTABIL", SqlDbType.VarChar, 255); cmd.Parameters.Add("@DS_CONTA_CONTABIL", SqlDbType.VarChar, 255); cmd.Parameters.Add("@ID_CONTA_CONTABIL", SqlDbType.VarChar,50); cmd.Parameters.Add("@DS_TRIMESTRE", SqlDbType.VarChar, 4); cmd.Parameters.Add("@DS_SEMESTRE", SqlDbType.VarChar, 4); cmd.Parameters.Add("@NR_TRIMESTRE", SqlDbType.Int); cmd.Parameters.Add("@NR_SEMESTRE", SqlDbType.Int); cmd.Parameters.Add("@NR_ANO", SqlDbType.Int); cmd.Parameters.Add("@NR_MES", SqlDbType.Int); cmd.Parameters.Add("@NM_FILIAL", SqlDbType.VarChar, 255); cmd.Prepare(); foreach (dtoVisaoBenner obj in lista) { cmd.Parameters["@DT_LANCAMENTO"].Value = obj.CTLDATA; cmd.Parameters["@ID_LANCAMENTO_CONTABIL"].Value = obj.CTLHANDLE.ToString(); cmd.Parameters["@NR_DOC_CONTABIL"].Value = obj.CTLDOCTO.ToString(); cmd.Parameters["@TP_LANCAMENTO_GERADO"].Value = obj.LANCAMENTOGERADO; cmd.Parameters["@VL_LANCAMENTO"].Value = obj.CTLANVALORF; cmd.Parameters["@TP_NATUREZA"].Value = obj.NATUREZA; cmd.Parameters["@CD_EMPRESA"].Value = obj.EMPRESA; cmd.Parameters["@CD_FILIAL"].Value = obj.FILIAL; cmd.Parameters["@CD_CONTA_CONTABIL"].Value = obj.CONTAHANDLE.ToString(); cmd.Parameters["@DS_CONTA_CONTABIL"].Value = obj.CONTANOME.ToString(); cmd.Parameters["@ID_CONTA_CONTABIL"].Value = obj.CONTA; cmd.Parameters["@DS_TRIMESTRE"].Value = obj.TRIMESTRE; cmd.Parameters["@DS_SEMESTRE"].Value = obj.SEMESTRE; cmd.Parameters["@NR_TRIMESTRE"].Value = obj.NRTRIMESTRE; cmd.Parameters["@NR_SEMESTRE"].Value = obj.NRSEMESTRE; cmd.Parameters["@NR_ANO"].Value = obj.NRANO; cmd.Parameters["@NR_MES"].Value = obj.NRMES; cmd.Parameters["@NM_FILIAL"].Value = obj.NOME; cmd.ExecuteNonQuery(); rowAffected++; }