使用for循环将多个记录插入SQL Server数据库

我正在使用C#编写Windows窗体项目。 我试图从数组中将多个记录插入SQL Server数据库。

进入第一行后,我得到一个例外

@UserID已经被声明了。 变量名在查询批处理或存储过程中必须是唯一的。

数据库中的主键没有问题,因为UserID不是主键。

这就是我想要做的。

 public static void featuresentry() { SqlConnection connection = new SqlConnection(HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString); SqlCommand command = new SqlCommand(); connection.Open(); try { command = connection.CreateCommand(); for (int i = 0; i < Details.modelKeyPoints.Size; i++) { command.CommandText = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)"; command.Parameters.AddWithValue("@UserID", Details.ID); command.Parameters.AddWithValue("@Angle", Convert.ToDouble(Details.modelKeyPoints[i].Angle)); command.Parameters.AddWithValue("@ClassID", Convert.ToDouble(Details.modelKeyPoints[i].ClassId)); command.Parameters.AddWithValue("@Octave", Convert.ToDouble(Details.modelKeyPoints[i].Octave)); command.Parameters.AddWithValue("@PointX", Convert.ToDouble(Details.modelKeyPoints[i].Point.X)); command.Parameters.AddWithValue("@PointY", Convert.ToDouble(Details.modelKeyPoints[i].Point.Y)); command.Parameters.AddWithValue("@Response", Convert.ToDouble(Details.modelKeyPoints[i].Response)); command.Parameters.AddWithValue("@Size", Convert.ToDouble(Details.modelKeyPoints[i].Size)); command.ExecuteNonQuery(); } } catch (Exception) { throw; } finally { if (connection.State == ConnectionState.Open) { connection.Close(); } } } 

你应该这样做:

  • 在循环外定义您的参数
  • 为每次迭代定义循环内参数的
  • 使用using(...) { ... }块来摆脱try ... catch ... finallyusing块将确保在不再需要时正确快速地处理类)
  • 停止使用try...catch如果你实际上没有处理exception – 只需重新抛出它们(没有意义)

试试这段代码:

 public static void featuresentry() { string connectionString = HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString; string insertQuery = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)"; using (SqlConnection connection = new SqlConnection(connectionString)) using (SqlCommand command = new SqlCommand(insertQuery, connection)) { // define your parameters ONCE outside the loop, and use EXPLICIT typing command.Parameters.Add("@UserID", SqlDbType.Int); command.Parameters.Add("@Angle", SqlDbType.Double); command.Parameters.Add("@ClassID", SqlDbType.Double); command.Parameters.Add("@Octave", SqlDbType.Double); command.Parameters.Add("@PointX", SqlDbType.Double); command.Parameters.Add("@PointY", SqlDbType.Double); command.Parameters.Add("@Response", SqlDbType.Double); command.Parameters.Add("@Size", SqlDbType.Double); connection.Open(); for (int i = 0; i < Details.modelKeyPoints.Size; i++) { // now just SET the values command.Parameters["@UserID"].Value = Details.ID; command.Parameters["@Angle"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Angle); command.Parameters["@ClassID"].Value = Convert.ToDouble(Details.modelKeyPoints[i].ClassId); command.Parameters["@Octave"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Octave); command.Parameters["@PointX"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.X); command.Parameters["@PointY"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.Y); command.Parameters["@Response"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Response); command.Parameters["@Size"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Size); command.ExecuteNonQuery(); } } } 

如果你把command = connection.CreateCommand(); 在你的for循环中,它会工作。 问题是你只是循环遍历命令参数,所以它试图在现有命令中添加更多参数,但它们已经在那里。 所以你需要在每个循环中创建一个新命令。

您需要在循环外添加Command参数或在循环内声明Command。

在第一种情况下,您需要更新每个参数的值,如下所示:

 oleDbCommand1.Parameters["@UserID"].Value = Details.ID; 

并在设置新值后执行命令。

为了获得最大性能,您可以考虑使用BulkInsert 。 这确保您的插入尽可能快地完成,因为任何已发出的查询都有一些开销(大型查询通常比许多小查询执行得更快)。 它应该类似于以下内容:

1)从这里定义AsDataTable扩展方法:

  public static DataTable AsDataTable(this IEnumerable data) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); var table = new DataTable(); foreach (PropertyDescriptor prop in properties) table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); foreach (T item in data) { DataRow row = table.NewRow(); foreach (PropertyDescriptor prop in properties) row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; table.Rows.Add(row); } return table; } 

2)像这样执行实际的BulkInsert( 未测试 ):

 using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); SqlTransaction transaction = connection.BeginTransaction(); using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction)) { bulkCopy.BatchSize = 100; bulkCopy.DestinationTableName = "dbo.FEATURES"; try { // define mappings for columns, as property names / generated data table column names // is different from destination table column name bulkCopy.ColumnMappings.Add("ID","UserID"); bulkCopy.ColumnMappings.Add("Angle","Angle"); // the other mappings come here bulkCopy.WriteToServer(Details.modelKeyPoints.AsDataTable()); } catch (Exception) { transaction.Rollback(); connection.Close(); } } transaction.Commit(); } 

当然,如果使用约定优于配置 (对象属性名称将与目标表列名称完全匹配),则不需要映射。

您可以通过将数据作为xml字符串发送并转换为sql中存储过程中的表来完成此操作。 例如:假设我在sql表中发送多行来添加/更新,那么这里是以下步骤:

  1. 使用以下方法将您的类或类列表转换为xml字符串:

     public static string SerializeObjectToXmlString(object value) { var emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }); var serializer = new XmlSerializer(value.GetType()); var settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; using (var stream = new StringWriter()) using (var writer = XmlWriter.Create(stream, settings)) { serializer.Serialize(writer, value, emptyNamepsaces); return stream.ToString(); } } 
  2. 现在,在将数据发送到数据库时,将类对象转换为xml字符串(这里我在代码中使用entity framework,您可以在不使用它的情况下执行此操作):

     bool AddUpdateData(List data) { bool returnResult = false; string datatXml = Helper.SerializeObjectToXmlString(data); var sqlparam = new List() { new SqlParameter() { ParameterName = "dataXml", Value = datatXml} }; var result = this.myEntity.Repository().ExecuteStoredProc("SQL_StoredProc", sqlparam); if (result != null && result.Count() > 0) { returnResult = result[0].Status == 1 ? true : false; } return returnResult; } 
  3. 现在你的SQL代码:

3.1声明一个表变量:

 DECLARE @tableVariableName TABLE ( ID INT, Name VARCHAR(20) ) 

3.2将您的xml字符串插入Table变量

 INSERT INTO @tableVariableName SELECT Finaldata.R.value ('(ID/text())[1]', 'INT') AS ID, Finaldata.R.value ('(Name/text())[1]', 'VARCHAR(20)') AS Name FROM @MyInputXmlString.nodes ('//ArrayMyClass/MyClass') AS Finaldata (R) 

3.3最后将此表值插入到sql表中

 INSERT INTO MyTable (ID, Name) SELECT ID, Name FROM @tableVariableName 

这将节省您使用for循环一次又一次地击中数据库的努力。

希望它会对你有所帮助