【问题标题】:Faster Way to do Inserts C# & SQL更快的插入方式 C# & SQL
【发布时间】:2013-12-21 06:22:43
【问题描述】:

我在想用 C# 甚至 SQL 查询执行批量 SQL 插入命令的方法时遇到了麻烦。

我通常会得到一个零件编号列表...有时它有 100 多个零件

LTM-120
LTM-130
LTM-140
LTM-120
LTM-130
LTM-140

而且,我使用一个按钮将它插入到 C# 中......

Textbox20 是 PartNumber,Textbox15 是 PartNumber ID

SqlConnection sqlCon3 = new SqlConnection("REMOVED");
SqlCommand sqlCmd3 = new SqlCommand();
sqlCmd3.CommandText = "INSERT INTO [Products].[Features] " +
                      "([ProductID] ,[Title] ,[ViewOrder]) VALUES ('" +
                      textBox15.Text + "', '" + textBox20.Text + "', NULL) ";
sqlCmd3.Connection = sqlCon3;

sqlCon3.Open();
sqlCmd3.ExecuteNonQuery();
sqlCon3.Close();

有没有办法批量查询一堆零件号?或者有人对如何做这样的事情有任何想法吗?

【问题讨论】:

  • 您的零件编号是在数组还是列表变量中?
  • 你还在使用 MySQL 还是 SQL Server?它们是两个不同的东西,因此请相应地调整您的标签以获得更相关的帮助。
  • SQL Server,很抱歉,它通常只是一个 txt 文件或 excel 表中的列表。
  • 与具体问题无关,但您可能应该查看Parameterized Queries,而不是将文本框的值直接嵌入到您的 SQL 中。
  • I really love your code。我想知道如果有人在您的textBox15 中输入A'); DROP TABLE Products;-- 会发生什么。

标签: c# sql sql-server


【解决方案1】:

您可以使用SqlBulkCopy 对数据库进行批量插入。

    // Create a table with some rows. 
    DataTable newProducts = MakeTable();

    // Create the SqlBulkCopy object.  
    // Note that the column positions in the source DataTable  
    // match the column positions in the destination table so  
    // there is no need to map columns.  
    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
    {
        bulkCopy.DestinationTableName = 
            "dbo.BulkCopyDemoMatchingColumns";

        try
        {
            // Write from the source to the destination.
            bulkCopy.WriteToServer(newProducts);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

见:MSDN

【讨论】:

  • 我不需要流式传输 txt 或 xls 文件吗?
  • @user3014698 不,你可以给它一个DataTable
【解决方案2】:

或者你可以使用更安全的参数

SqlConnection sqlCon3 = new SqlConnection("REMOVED");
                SqlCommand sqlCmd3 = new SqlCommand();
                sqlCmd3.CommandText = "INSERT INTO [Products].[Features] ([ProductID] ,[Title] ,[ViewOrder]) VALUES (@myID, @myTitle, NULL) ";
                sqlCmd3.Connection = sqlCon3;

                sqlCon3.Open();
                for (int i = 0; i < 10; i++)
                {
                    sqlCmd3.Parameters.Add("myID", SqlDbType.Int).Value = myobjID[i];
                    sqlCmd3.Parameters.Add("myTitle", SqlDbType.Text).Value = myobjTitle[i];
                    sqlCmd3.ExecuteNonQuery();
                    sqlCmd3.Parameters.Clear();
                }

                sqlCon3.Close();

你也可以使用交易Executing query with parameters

【讨论】:

    【解决方案3】:

    有许多不同的技术可以同时执行多个 INSERT。其他答案已经讨论了这些(SqlBulkCopy、BULK INSERT 等)

    您还可以构建一次执行多个 INSERT 的 SQL 语句。例如,使用INSERT VALUES,将多个 VALUES 子句串在一起:

    INSERT INTO [Products].[Features] ([ProductID], [Title], [ViewOrder])
    VALUES (1, 'Title 1', NULL),
        (2, 'Title 2', NULL),
        -- etc.
    

    或将 INSERT 与 UNION 一起使用:

    INSERT INTO [Products].[Features] ([ProductID], [Title], [ViewOrder])
    SELECT 1, 'Title 1', NULL
    UNION ALL SELECT 2, 'Title 2', NULL
    UNION ALL -- etc.
    

    但是,您确实需要小心您的参数。正如其他人指出的那样,您当前的代码容易受到 SQL 注入攻击。

    【讨论】:

      【解决方案4】:

      您可以读入一个数组并通过一次插入一行来循环。我确信有比这更好的解决方案,但它会起作用。

      伪代码-

      Fill array from file
      
      for(count int = 0; count < arr.length; count++)
      {
        sqlcon.open()
        sqlcmd = New select statment with arr[count] values
        sqlcmd.execute
        sqlcon.close()
      }
      

      基本上将您的数据放入数组中,循环遍历数组,提取每个数据值并插入到您的数据库中。如果您有错误的数据,这可能会导致问题。您可能想对 SQL 注入和参数进行一些研究,以实现一些方法来确保您的数据库不会损坏。

      编辑:

      虽然每次紧密连接更安全,但此效果速度会提高多少?我假设每次打开和关闭连接都会比循环前打开和数据处理完成后关闭慢。

      编辑 2:

      如上所述,但不是从静态文件填充数组,而是通过实现此处所示的浏览/打开文件功能从用户选择的文件填充数组

      http://msdn.microsoft.com/en-us/library/cc221415(v=vs.95).aspx

      那么你的INSERT 语句对于每个值都将保持不变,除了插入的新数据值。

      【讨论】:

      • 我建议您最好使用using 语句。至少,在退出循环之前关闭连接。
      • @FrazellThomas 是的,我同意。可以对伪代码做很多事情以使其在效率、可靠性和完整性方面变得更好,但我想给 OP 一个前进的方向。自 C# 以来已经有一段时间了,所以我的函数调用和语法有点生疏,因此是伪代码而不是工作代码解决方案。
      • 没问题。我只是提出了一个编辑建议,但文字使它听起来不如我预期的好。此外,.NET 使用连接池,因此打开和关闭连接比耗尽连接池更快。
      • @FrazellThomas 我不知道。感谢您的信息。
      • 我想做的是从列表中零件的 txt 文件中读取,并专门为所有这些零件号插入查询
      【解决方案5】:

      批量复制当然是一种方法。

      但是,由于您的数据集相对较小(100 项),您可以考虑使用 SQL Server 对 XML 的支持。给定这样的表:

      create table dbo.item
      (
        id    int          not null primary key clustered ,
        name  varchar(100) not null unique nonclustered ,
      )
      

      你可以这样写一个存储过程:

      create procedure dbo.insert_items
      
        @item_list xml
      
      as
      
        insert dbo.item
        (
          id   ,
          name
        )
        select id   =              X.value( './id[1]'   , 'int'          )    ,
               name = ltrim(rtrim( X.value( './name[1]' , 'varchar(100)' ) ))
        from @item_list.nodes('/items/item') as itemList(X) 
      
        return @@rowcount
      go
      

      可以这样调用(在 SQL Server Management Studio 中):

      exec insert_items '
        <items>
          <item><id> 1 </id><name> alpha   </name></item>
          <item><id> 2 </id><name> bravo   </name></item>
          <item><id> 3 </id><name> charlie </name></item>
          <item><id> 4 </id><name> delta   </name></item>
        </items>
        '
      

      或者,在 C# 中,像这样:

      string connect_string = "Server=localhost;Database=sandbox;Trusted_Connection=True;" ;
      
      string myXmlString    = @"
        <items>
          <item><id> 1 </id><name> alpha   </name></item>
          <item><id> 2 </id><name> bravo   </name></item>
          <item><id> 3 </id><name> charlie </name></item>
          <item><id> 4 </id><name> delta   </name></item>
        </items>
      " ;
      
      using ( SqlConnection conn = new SqlConnection(connect_string) )
      using ( SqlCommand    cmd  = conn.CreateCommand() )
      {
        cmd.CommandText = "dbo.insert_items" ;
        cmd.CommandType = CommandType.StoredProcedure ;
        cmd.Parameters.AddWithValue( "@item_list" , myXmlString ) ;
        conn.Open() ;
        cmd.ExecuteNonQuery() ;
        conn.Close() ;
      }
      

      然后是获取您的零件编号列表并将其序列化为合适的 XML(如果还没有)并在您的存储过程中构建正确的 XQuery/XPath 以提取必要的位。

      在现实世界中,由于 XML 处理可能......既昂贵又痛苦,我发现最简单的方法是让存储过程首先使用 OpenXml() 将 XML 加载到临时表中,以便处理 XML不碍事。完成后,存储过程的其余部分可以是直接的 SQL。它也简化了调试和维护。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-21
        • 1970-01-01
        • 1970-01-01
        • 2018-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-10
        相关资源
        最近更新 更多