【问题标题】:SQL Insert one row or multiple rows data?SQL插入一行或多行数据?
【发布时间】:2010-11-04 16:17:15
【问题描述】:

我正在开发一个控制台应用程序以将数据插入 MS SQL Server 2005 数据库。我有一个要插入的对象列表。这里我以Employee类为例:

List<Employee> employees;

我能做的就是像这样一次插入一个对象:

foreach (Employee item in employees)
{
  string sql = @"INSERT INTO Mytable (id, name, salary) 
    values ('@id', '@name', '@salary')";
  // replace @par with values
  cmd.CommandText = sql; // cmd is IDbCommand
  cmd.ExecuteNonQuery();
}

或者我可以像这样构建一个插入查询:

string sql = @"INSERT INTO MyTable (id, name, salary) ";
int count = employees.Count;
int index = 0;
foreach (Employee item in employees)
{
   sql  = sql + string.format(
     "SELECT {0}, '{1}', {2} ",
     item.ID, item.Name, item.Salary);
   if ( index != (count-1) )
      sql = sql + " UNION ALL ";
   index++
 }
 cmd.CommandType = sql;
 cmd.ExecuteNonQuery();

我猜后一种情况是一次插入多行数据。但是,如果我有 几ks的数据,SQL查询字符串有限制吗?

我不确定在性能方面,一次插入多行是否优于一次插入一行数据?

有什么更好的建议吗?

【问题讨论】:

    标签: c# sql sql-server sql-server-2005 tsql


    【解决方案1】:

    实际上,按照您编写的方式,您的第一个选项会更快。

    1. 您的第二个示例存在问题。您正在执行 sql = + sql + 等。这将导致为循环的每次迭代创建一个新的字符串对象。 (查看 StringBuilder 类)。从技术上讲,您也将在第一个实例中创建一个新的字符串对象,但不同之处在于它不必复制前一个字符串选项中的所有信息。

    2. 按照您的设置方式,当您最终发送一个庞大的查询时,SQL Server 将不得不潜在地评估它,这肯定需要一些时间来弄清楚它应该做什么。我应该说,这取决于你需要做多少插入。如果 n 很小,你可能会没事,但随着它的增长,你的问题只会变得更糟。

    由于 SQL Server 处理批处理事务的方式,批量插入比单个插入更快。如果您要从 C# 插入数据,您应该采用第一种方法,并将每 500 次插入包装到事务中并提交它,然后执行下一个 500 次,依此类推。这还有一个好处是,如果批处理失败,您可以捕获它们并找出问题所在并重新插入那些。还有其他方法可以做到这一点,但这肯定会比提供的两个示例有所改进。

    var iCounter = 0;
    foreach (Employee item in employees)
    {
    
       if (iCounter == 0)
      {
        cmd.BeginTransaction;
      }
      string sql = @"INSERT INTO Mytable (id, name, salary) 
        values ('@id', '@name', '@salary')";
      // replace @par with values
      cmd.CommandText = sql; // cmd is IDbCommand
      cmd.ExecuteNonQuery();
      iCounter ++;
      if(iCounter >= 500)
      {
         cmd.CommitTransaction;
         iCounter = 0;
      }
    }
    
    if(iCounter > 0)
       cmd.CommitTransaction;
    

    【讨论】:

    • 调用SP而不是INERT sql怎么样? SP 将采用类似的参数,但在 SP 内,每个调用都有提交。如果任何 SP 调用失败,事务是否会回滚?
    • @Kevin,为什么 CommitTransaction 没有出现在 Intellisense 中?
    • 说实话,我不知道。我必须追查为什么@Spike
    • @Kevin 不用担心 :) 这只是一个想法。
    【解决方案2】:

    在 MS SQL Server 2008 中,您可以创建包含您的表的 .Net table-UDT

    CREATE TYPE MyUdt AS TABLE (Id int, Name nvarchar(50), salary int)
    

    然后,您可以在存储过程中使用此 UDT,并在您的 с#-code 中使用批量插入。 SP:

    CREATE PROCEDURE uspInsert
     (@MyTvp AS MyTable READONLY)
    AS
         INSERT INTO [MyTable]
          SELECT * FROM @MyTvp
    

    C#(假设您需要插入的记录已包含在 DataSet ds 的“MyTable”表中):

    using(conn)
    {
        SqlCommand cmd = new SqlCommand("uspInsert", conn);
        cmd.CommandType = CommandType.StoredProcedure;
    
        SqlParameter myParam = cmd.Parameters.AddWithValue
         ("@MyTvp", ds.Tables["MyTable"]);
    
        myParam.SqlDbType = SqlDbType.Structured;
        myParam.TypeName = "dbo.MyUdt";
    
        // Execute the stored procedure
        cmd.ExecuteNonQuery();
    }
    

    所以,这就是解决方案。

    最后我想阻止你使用像你这样的代码(构建字符串然后执行这个字符串),因为这种执行方式可能用于 SQL 注入。

    【讨论】:

      【解决方案3】:

      this thread, 我已经回答了有关表值参数的问题。

      【讨论】:

        【解决方案4】:

        Bulk-copy 通常比自己插入要快。

        如果您仍想以您建议的方式之一执行此操作,您应该这样做,以便您可以轻松更改发送到服务器的查询的大小。这样您就可以在以后优化生产环境中的速度。查询时间可能因查询大小而异。

        【讨论】:

          【解决方案5】:

          SQL Server 查询的批处理大小为 65,536 * 网络数据包大小。网络数据包大小默认为 4kbs,但可以更改。查看 SQL 2008 的 Maximum capacity article 以获取范围。 SQL 2005 似乎也有同样的限制。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-08-24
            • 1970-01-01
            • 1970-01-01
            • 2013-01-01
            • 2012-09-04
            相关资源
            最近更新 更多