在我的数据库表中插入数据表记录时出错

时间:2016-06-22 13:49:47

标签: c# .net datatable ado.net dataadapter

我是ado.net的新手,目前正在将数据表记录插入我的数据库表。

我有一个包含一些数据的Excel文件,我在这个Excel文件中创建一个包含大量数据表的数据集。

在这个数据集中,我有两个数据表形式:

带有记录的数据表0:类别

ParentCategory Description
  Electronics   jhdkhsd
  Sports        kjshfhs

带有记录的数据表1:子类别

Subcategory ParentCategory  Description
  Mobile       Electronics   weprwp
  Tv           Electronics   sdflskd
  Balls        Sports        kjshdfkjh
  Shoes        Sports        uytuyt

现在我的数据库表是这样的:

Category:Id,Name,parentid

所以基本上我试图在我的数据库表中插入所有这些数据表数据,这些数据是类数据表和SubCategory数据表,但是当我试图插入获取错误时:

  

错误:参数化查询'(@Id int output,@ ParentCategory   nvarchar(50))插入Category'期望参数   '@ ParentCategory',未提供。

到目前为止,这是我的代码:

 var dsFinal = new DataSet();

    //Some code to read Excel sheets and data from Excel and create datatables and records with it.


    //code to insert records
     using (SqlConnection connection = new SqlConnection(""))
     {
       SqlDataAdapter adapter = new SqlDataAdapter();
       var insertCommand = new SqlCommand("insert into Category (Name) values (@ParentCategory) SET @Id = SCOPE_IDENTITY()", connection);
      var parameter = insertCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "Id");
      insertCommand.Parameters.Add("@ParentCategory", SqlDbType.NVarChar, 50, "Name");
      parameter.Direction = ParameterDirection.Output;
     insertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
     adapter.InsertCommand = insertCommand;
    adapter.Update(dsFinal .Tables[0]);
   }

这里我添加了所有类别数据表行rowstate state属性,所以我直接尝试插入所有类别记录。

我是否必须循环到单个记录并插入?由于我拥有数千个类别及其子类别,并且这样做会使我的系统变慢。

1 个答案:

答案 0 :(得分:1)

使用以下代码段。

  • 您需要在SQL for insert命令中的INSETRT和SCOPE_IDENTITY语句之间指定一个分号。
  • 此外,由于您的基表包含ID,Name,ParentId列,因此您必须将所有这些列映射到insert命令,并且只插入Name和ParentId列,因为ID是自动生成的。 ID列映射到输出参数,而其他列映射到输入参数。

使用DataTable

将单行插入数据库
     using(SqlConnection connection = new SqlConnection("")) {
      SqlDataAdapter adapter = new SqlDataAdapter();
      var insertCommand = new SqlCommand("Insert into Category (Name, ParentId) Values (@name, @parentId); SET @ID = SCOPE_IDENTITY(); ", connection);
      var parameter = insertCommand.Parameters.Add("@name", SqlDbType.NVarChar, 50, "Name");
      insertCommand.Parameters.Add("@parentId", SqlDbType.Int, 0, "ParentId");
      SqlParameter parameter = adapter.InsertCommand.Parameters.Add("@ID",SqlDbType.Int, 0, "ID");
      parameter.Direction = ParameterDirection.Output;
      adapter.insertCommand = insertCommand;
      adapter.insertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
      adapter.Update(dsControlSheet.Tables[0]);
    }

以上内容应该注意您在帖子中提到的错误消息。

下面的代码片段将帮助您批量插入行,而不是一次需要在数据库中插入许多行时插入行。对于批处理插入语句,您需要将adpapter.UpdateBatchSize指定为大于1的值。

批量从DataTable

将行插入数据库
 using (SqlConnection connection = new SqlConnection(""))
 {
   SqlDataAdapter adapter = new SqlDataAdapter();
   var insertCommand = new SqlCommand("Insert into Category (Name, ParentId) Values (@name, @parentId);", connection);
   var parameter = insertCommand.Parameters.Add("@name", SqlDbType.NVarChar, 50, "Name");
   insertCommand.Parameters.Add("@parentId", SqlDbType.Int, 0, "ParentId");
   adapter.insertCommand = insertCommand;
   // When setting UpdateBatchSize to a value other than 1, all the commands 
   // associated with the SqlDataAdapter have to have their UpdatedRowSource 
   // property set to None or OutputParameters. An exception is thrown otherwise.
     insertCommand.UpdatedRowSource = UpdateRowSource.None;
   // Gets or sets the number of rows that are processed in each round-trip to the server.
   // Setting it to 1 disables batch updates, as rows are sent one at a time.
    adapter.UpdateBatchSize = 50;
    adapter.Update(dsControlSheet.Tables[0]);
}

在进行批量插入时,需要记住几点。

  1. insert命令的CommandTimeout应该足够大以允许批量插入否则最终会出现超时异常。如果将此超时设置为0,则插入所允许的时间是不确定的。
  2. 在进行批量插入时,您希望获得最大的性能,否则您的插入可能会变得太慢。通过将批量插入作为单个事务执行,您将实现这种提高的性能。如果没有事务,数据库将为批处理中的每个INSERT启动一个事务,这需要更多时间。例如,如果批量大小为500(即adapter.UpdateBatchSize),那么如果insert命令没有附加任何事务,则500 INSERT语句将导致数据库中的500个事务;但是如果你附加一个事务来插入命令,那么所有500个INSERT只有1个事务,这会使性能提升。
  3. 从DataTable批量插入数据库的高性能

     using (SqlConnection connection = new SqlConnection(connectionString))
     {
       SqlDataAdapter adapter = new SqlDataAdapter();
       var insertCommand = new SqlCommand("Insert into Category (Name, ParentId) Values (@name, @parentId);", connection);
       var parameter = insertCommand.Parameters.Add("@name", SqlDbType.NVarChar, 50, "Name");
       insertCommand.Parameters.Add("@parentId", SqlDbType.Int, 0, "ParentId");
       adapter.insertCommand = insertCommand;
       // When setting UpdateBatchSize to a value other than 1, all the commands 
       // associated with the SqlDataAdapter have to have their UpdatedRowSource 
       // property set to None or OutputParameters. An exception is thrown otherwise.
         insertCommand.UpdatedRowSource = UpdateRowSource.None;
       // Gets or sets the number of rows that are processed in each round-trip to the server.
       // Setting it to 1 disables batch updates, as rows are sent one at a time.
        adapter.UpdateBatchSize = 50;
        //NOTE: When doing batch updates it's a good idea to fine tune CommandTimeout value
        //since default is 30 seconds. If your batch insert takes more than 30 s (default value)
        //then make sure to increase this value. I am setting this to 90 s
        //but you must decide this based on your situation.
        //Set this to 0 if you are not sure how long your batch inserts will take
        insertCommand.CommandTimeout = 90;
    
        //HOW TO MAKE BATCH INSERTS FASTER IN PERFORMANCE
        //Perform batch updates in a single transaction to increase batch insert performance
        connection.Open();
        var transaction = connection.BeginTransaction();
        insertCommand.Transaction = transaction;
        try { 
             adapter.Update(dsControlSheet.Tables[0]);
             transaction.Commit();
        }
        catch(Exception e) {
    
        if(transaction!=null) {
           transaction.Rollback();
         }
         //log exception
       }
       finally {
          connection.Close();
       }
    }