【问题标题】:How to insert a data table into SQL Server database table?如何将数据表插入到 SQL Server 数据库表中?
【发布时间】:2012-02-22 21:55:19
【问题描述】:

我已从某个 Excel 文件导入数据,并将其保存到 datatable。现在我想将此信息保存在我的SQL Server 数据库中。

我在网上看到很多资料,但我看不懂:

  1. 有人说逐行插入另一个建议的批量更新...等等:哪个更好?
  2. 我应该使用OLESQL Server 对象(如dataAdapterconnection)吗?

我需要从他的 Excel 文件中读取员工每周工时报告并将其保存到保存所有报告的数据库表中(每周使用新记录更新数据库)。

Excel 文件仅包含本周的报告。

【问题讨论】:

    标签: c# sql-server datatable


    【解决方案1】:

    在您的数据库中创建一个User-Defined TableType

    CREATE TYPE [dbo].[MyTableType] AS TABLE(
        [Id] int NOT NULL,
        [Name] [nvarchar](128) NULL
    )
    

    并在您的Stored Procedure 中定义一个参数:

    CREATE PROCEDURE [dbo].[InsertTable]
        @myTableType MyTableType readonly
    AS
    BEGIN
        insert into [dbo].Records select * from @myTableType 
    END
    

    并将您的DataTable 直接发送到 sql server:

    using (var command = new SqlCommand("InsertTable") {CommandType = CommandType.StoredProcedure})
    {
        var dt = new DataTable(); //create your own data table
        command.Parameters.Add(new SqlParameter("@myTableType", dt));
        SqlHelper.Exec(command);
    }
    

    要编辑存储过程中的值,您可以声明一个具有相同类型的局部变量并将输入表插入其中:

    DECLARE @modifiableTableType MyTableType 
    INSERT INTO @modifiableTableType SELECT * FROM @myTableType
    

    然后,您可以编辑@modifiableTableType

    UPDATE @modifiableTableType SET [Name] = 'new value'
    

    【讨论】:

    • 这是我自己的类,它是一个有连接的辅助类,在Exec方法中执行一个sql命令,我用它来简化我的代码。
    • 有趣,但是,如果我想重新打开该数据表或编辑数据表中的值,我该怎么做?
    • 要编辑存储过程中的值,您可以声明一个具有相同类型的局部变量并将输入表插入其中,即declare @modifiableTableType MyTableType然后insert into @modifiableTableType select * from @myTableType。然后,您可以编辑@modifiableTableType
    • 最佳答案!简单整洁。非常感谢。有更新吗(看这篇文章的日期)?
    • 能否请您提一下在哪里可以找到 sqlhelper 函数,Microsoft.ApplicationBlocks.Data 类中似乎不存在此函数
    【解决方案2】:

    如果您是第一次保存数据表

    执行此操作(使用批量复制)。确保没有 PK/FK 约束

    SqlBulkCopy bulkcopy = new SqlBulkCopy(myConnection);
    //I assume you have created the table previously
    //Someone else here already showed how  
    bulkcopy.DestinationTableName = table.TableName;
    try                             
    {                                 
        bulkcopy.WriteToServer(table);                            
    }     
        catch(Exception e)
    {
        messagebox.show(e.message);
    } 
    

    现在,因为您已经有了基本记录。你只想用现有的记录检查新记录。你可以这样做。

    这基本上会从数据库中获取现有表

    DataTable Table = new DataTable();
    
    SqlConnection Connection = new SqlConnection("ConnectionString");
    //I assume you know better what is your connection string
    
    SqlDataAdapter adapter = new SqlDataAdapter("Select * from " + TableName, Connection);
    
    adapter.Fill(Table);
    

    然后把这个表传给这个函数

    public DataTable CompareDataTables(DataTable first, DataTable second)
    {
        first.TableName = "FirstTable";
        second.TableName = "SecondTable";
    
        DataTable table = new DataTable("Difference");
    
        try
        {
            using (DataSet ds = new DataSet())
            {
                ds.Tables.AddRange(new DataTable[] { first.Copy(), second.Copy() });
    
                DataColumn[] firstcolumns = new DataColumn[ds.Tables[0].Columns.Count];
    
                for (int i = 0; i < firstcolumns.Length; i++)
                {
                    firstcolumns[i] = ds.Tables[0].Columns[i];
                }
    
                DataColumn[] secondcolumns = new DataColumn[ds.Table[1].Columns.Count];
    
                for (int i = 0; i < secondcolumns.Length; i++)
                {
                    secondcolumns[i] = ds.Tables[1].Columns[i];
                }
    
                DataRelation r = new DataRelation(string.Empty, firstcolumns, secondcolumns, false);
    
                ds.Relations.Add(r);
    
                for (int i = 0; i < first.Columns.Count; i++)
                {
                    table.Columns.Add(first.Columns[i].ColumnName, first.Columns[i].DataType);
                }
    
                table.BeginLoadData();
    
                foreach (DataRow parentrow in ds.Tables[0].Rows)
                {
                    DataRow[] childrows = parentrow.GetChildRows(r);
                    if (childrows == null || childrows.Length == 0)
                        table.LoadDataRow(parentrow.ItemArray, true);
                }
    
                table.EndLoadData();
    
            }
        }
    
        catch (Exception ex)
        {
            throw ex;
        }
    
        return table;
    }
    

    这将返回一个新的 DataTable,其中更新了更改的行。请确保您正确调用该函数。 DataTable first 应该是最新的。

    然后用这个新的数据表再次重复大容量复制功能。

    【讨论】:

    • 这是一个很好的解决方案,但是如果将列数据包含在导出中以 (dt.WriteXml(exportpath, XmlWriteMode.WriteSchema);) 开头,则可以省去必须定义列数据的麻烦.
    【解决方案3】:

    我给出了一个非常简单的代码,我在我的解决方案中使用了它(我的问题陈述和你的一样)

        SqlConnection con = connection string ;
    //new SqlConnection("Data Source=.;uid=sa;pwd=sa123;database=Example1");
    con.Open();
    string sql = "Create Table abcd (";
    foreach (DataColumn column in dt.Columns)
    {
        sql += "[" + column.ColumnName + "] " + "nvarchar(50)" + ",";
    }
    sql = sql.TrimEnd(new char[] { ',' }) + ")";
    SqlCommand cmd = new SqlCommand(sql, con);
    SqlDataAdapter da = new SqlDataAdapter(cmd);
    cmd.ExecuteNonQuery();
    using (var adapter = new SqlDataAdapter("SELECT * FROM abcd", con)) 
    using(var builder = new SqlCommandBuilder(adapter))
    {
    adapter.InsertCommand = builder.GetInsertCommand();
    adapter.Update(dt);
    // adapter.Update(ds.Tables[0]); (Incase u have a data-set)
    }
    con.Close();
    

    我已经给出了一个预定义的表名“abcd”(你必须注意这个名称的表在你的数据库中不存在)。 如果它对你有用,请投票给我的答案!!!! :)

    【讨论】:

    • 这个解决方案对我不起作用,我在“dt”变量中有很多数据,当更新函数运行时没有任何反应,在 VS 中调试模式下的执行通过行如此之快就像它什么都不做,也不例外。
    【解决方案4】:

    我建议您按照本文中的建议进行批量插入: Bulk Insertion of Data Using C# DataTable and SQL server OpenXML function

    【讨论】:

      【解决方案5】:
      public bool BulkCopy(ExcelToSqlBo objExcelToSqlBo, DataTable dt, SqlConnection conn, SqlTransaction tx)
      {
          int check = 0;
          bool result = false;
          string getInsert = "";
          try
          {
              if (dt.Rows.Count > 0)
              {
                  foreach (DataRow dr in dt.Rows)
                  {
                      if (dr != null)
                      {
                          if (check == 0)
                          {
                              getInsert = "INSERT INTO [tblTemp]([firstName],[lastName],[Father],[Mother],[Category]" +
                                      ",[sub_1],[sub_LG2])"+
                                      " select '" + dr[0].ToString() + "','" + dr[1].ToString() + "','" + dr[2].ToString() + "','" + dr[3].ToString() + "','" + dr[4].ToString().Trim() + "','" + dr[5].ToString().Trim() + "','" + dr[6].ToString();
      
                              check += 1;
                          }
                          else
                          {
                              getInsert += " UNION ALL ";
      
                              getInsert += " select  '" + dr[0].ToString() + "','" + dr[1].ToString() + "','" + dr[2].ToString() + "','" + dr[3].ToString() + "','" + dr[4].ToString().Trim() + "','" + dr[5].ToString().Trim() + "','" + dr[6].ToString() ;
      
                              check++;
                          }
                      }
                  }
                  result = common.ExecuteNonQuery(getInsert, DatabasesName, conn, tx);
              }
              else
              {
                  throw new Exception("No row for insertion");
              }
              dt.Dispose();
          }
          catch (Exception ex)
          {
              dt.Dispose();
              throw new Exception("Please attach file in Proper format.");
          }
          return result;
      } 
      

      【讨论】:

      • 这不是一个好方法。如果其中一个 DataRows 包含单引号,您会中断 SQL 连接并引入 SQL 注入攻击的可能性。
      【解决方案6】:
          //best way to deal with this is sqlbulkcopy 
          //but if you dont like it you can do it like this
          //read current sql table in an adapter
          //add rows of datatable , I have mentioned a simple way of it
          //and finally updating changes
      
          Dim cnn As New SqlConnection("connection string")        
          cnn.Open()
          Dim cmd As New SqlCommand("select * from  sql_server_table", cnn)
          Dim da As New SqlDataAdapter(cmd)       
          Dim ds As New DataSet()
          da.Fill(ds, "sql_server_table")
          Dim cb As New SqlCommandBuilder(da)        
      
          //for each datatable row
          ds.Tables("sql_server_table").Rows.Add(COl1, COl2)
      
          da.Update(ds, "sql_server_table")
      

      【讨论】:

        【解决方案7】:

        我发现如果您的表有主键,最好逐行添加到表中。一次插入整个表会在自动增量上产生冲突。

        这是我存储的过程

        CREATE PROCEDURE dbo.usp_InsertRowsIntoTable
        @Year       int,
        @TeamName   nvarchar(50),
        AS
        INSERT INTO [dbo.TeamOverview]
        (Year,TeamName)
        VALUES (@Year, @TeamName);
        RETURN
        

        我将这段代码放在一个循环中,用于我需要添加到表中的每一行:

        insertRowbyRowIntoTable(Convert.ToInt16(ddlChooseYear.SelectedValue), name);
        

        这是我的数据访问层代码:

                public void insertRowbyRowIntoTable(int ddlValue, string name)
            { 
                SqlConnection cnTemp = null;
                string spName = null;
                SqlCommand sqlCmdInsert = null;
        
                try
                {
                    cnTemp = helper.GetConnection();
                    using (SqlConnection connection = cnTemp)
                    {
                        if (cnTemp.State != ConnectionState.Open)
                            cnTemp.Open();
                        using (sqlCmdInsert = new SqlCommand(spName, cnTemp))
                        {
                            spName = "dbo.usp_InsertRowsIntoOverview";
                            sqlCmdInsert = new SqlCommand(spName, cnTemp);
                            sqlCmdInsert.CommandType = CommandType.StoredProcedure;
        
                            sqlCmdInsert.Parameters.AddWithValue("@Year", ddlValue);
                            sqlCmdInsert.Parameters.AddWithValue("@TeamName", name);
        
                            sqlCmdInsert.ExecuteNonQuery();
        
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    if (sqlCmdInsert != null)
                        sqlCmdInsert.Dispose();
        
                    if (cnTemp.State == ConnectionState.Open)
                        cnTemp.Close();
                }
        
            }
        

        【讨论】:

        • 欢迎来到 SO!您能否详细说明为什么您认为逐行执行此操作更好?这是一个相当古老的问题,它已经有一个公认的答案。与现有答案相比,新答案应该有所改进。
        • 当我尝试这样做时,我遇到了主键问题。它会给我一个错误,说类似“...无法插入主键,除非 IDENTITY_INSERT 为 ON。这意味着它不能自动递增和整个表。我尝试了 SCOPE_IDENTITY 但它没用。如果你不需要主键插入整个表格工作正常。
        • 这里有不必要的开销。每次将一行插入数据库时​​,您都在建立一个新连接,发出命令并处理连接。在循环中这样做是低效的。
        • @RedTaz,这不是真的。当您使用“Using”语句时,您只打开一次连接。这不是一个循环。它用于确保始终执行“Dispose”命令。 msdn.microsoft.com/en-us/library/yh598w02.aspx
        • 我还应该在我的初始评论中添加一个警告,即每次启用 SQL Server Connection Pooling 时都不会建立新连接(默认情况下)
        【解决方案8】:

        根据我对问题的理解,这可以使用一个相当直接的解决方案。无论如何下面是我提出的方法,这种方法接受一个数据表,然后使用 SQL 语句插入到数据库中的一个表中。请注意我的解决方案是使用 MySQLConnection 和 MySqlCommand 将其替换为 SqlConnection 和 SqlCommand。

        public void InsertTableIntoDB_CreditLimitSimple(System.Data.DataTable tblFormat)
            {
                for (int i = 0; i < tblFormat.Rows.Count; i++)
                {
        
                    String InsertQuery = string.Empty;
        
                    InsertQuery = "INSERT INTO customercredit " +
                                  "(ACCOUNT_CODE,NAME,CURRENCY,CREDIT_LIMIT) " +
                                  "VALUES ('" + tblFormat.Rows[i]["AccountCode"].ToString() + "','" + tblFormat.Rows[i]["Name"].ToString() + "','" + tblFormat.Rows[i]["Currency"].ToString() + "','" + tblFormat.Rows[i]["CreditLimit"].ToString() + "')";
        
        
        
                    using (MySqlConnection destinationConnection = new MySqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ToString()))
                    using (var dbcm = new MySqlCommand(InsertQuery, destinationConnection))
                    {
                        destinationConnection.Open();
                        dbcm.ExecuteNonQuery();
                    }
                }
            }//CreditLimit
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-29
          • 2015-06-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多