【问题标题】:Fastest way to insert many rows插入多行的最快方法
【发布时间】:2015-11-03 11:20:11
【问题描述】:

我有一个DataTable,其中包含数十万行。在我的过程中,我向这个 DataTable 添加了几千行,这些行也需要添加到数据库中。我不想为每条记录创建一个 INSERT 语句,而是希望尽快插入它们。 MySQL LOAD INTO 命令不适合,因为我不想涉及任何外部 CSV 文件。

到目前为止,我所做的是使用 MySqlDataAdapter 并调用“更新”方法,只对插入进行更改,如下所示:

MySqlCommandBuilder commandBuilder = new MySqlCommandBuilder(adapter);
adapter.InsertCommand = commandBuilder.GetInsertCommand();
adapter.Update(myDataTable);

这也运行得非常缓慢,所以我怀疑它们也是一次插入一行。我有什么选择?构建一个包含所有值的长 INSERT 语句是唯一的方法吗?

【问题讨论】:

  • 在更新之前手动启动事务有帮助吗?
  • @Thomas - 批量插入似乎只能使用 MySQL 中的外部文件,对吗?

标签: c# mysql sql .net database


【解决方案1】:

插入类似的值:-

INSERT INTO tbl_name
    (a,b,c)
VALUES
    (1,2,3),
    (4,5,6),
    (7,8,9);

为了优化插入速度,将许多小操作组合成一个 大手术。

【讨论】:

  • 如何使用参数化查询来做到这一点?它是否能够支持数十万条记录?
  • 列上有索引吗?
  • 并非所有这些都被编入索引@avrono
  • 您可以为此使用参数化查询,但代码会变得更加混乱。我不会在每个 INSERT 上使用大量记录,因为这种方法的好处会随着更大的记录而减少。我通常会在每次插入时获取约 250 条记录。在 PHP 中,我使用一个类进行插入,将数据行传递给它以进行插入。然后该类负责构建查询以及何时插入,并在调用对象析构函数时插入最终记录。
  • 我强烈建议您对此进行测试/比较以创建一个 csv 文件并将其删除。执行约 400-1600 个查询不会比在服务器端使用 MySqlBulkLoader 创建文件并在之后删除它快。同样,这应该是确定的,
【解决方案2】:

我看到的唯一解决方案是:

1) 将DataTable转成csv->你可以google一下。

2) 将其保存在服务器端的临时目录中。

3) 使用MySqlBulkLoader 这里是link 来写文章。加载保存在 temp 目录中的文件。

4) 之后从临时目录中删除该文件。

【讨论】:

  • 虽然我一直都知道这个选项,但这是我最终采用的方法,因为没有其他明显的方法可以实现我想要实现的目标。因此,感谢您确认这是要走的路。
【解决方案3】:

我不知道这是否是一个带参数的好方法,但它工作得很好 该方法接收“ParamDbList”列表(ParamDB 集合)并每 1000 个寄存器或 1900 个参数(限制为 2000 个)插入行。只需根据您的驱动器调整它

  public bool InsertBatch(System.Collections.Generic.List<ParamDbLIST> dados, string tabela)
    {
        if (dados.Count == 0)
            return true;

        string campos = "";
        dados[0].ForEach(delegate(ParamDB p)
        {
            campos += (campos == "" ? "" : ", ") + "@" + p.sNOME + "#N#";
        });

        bool resultado = true;
        //Insere de 999 a 999, que é o máximo q o sql server permite por vez
        //Maximo de 2000 parametros
        int k = 0;
        while (k < dados.Count)
        {
            this.sql = new StringBuilder();
            List<String> vals = new List<string>();
            ParamDbLIST parametros_insert = new ParamDbLIST();
            int c_sqls = 0;
            int c_parametros = 0;
            while (k < dados.Count && c_sqls < 1000 && c_parametros < 1900)
            {
                c_sqls++;
                vals.Add("(" + campos.Replace("#N#", c_sqls.ToString()) + ")");
                foreach (ParamDB p in dados[k])
                {

                    p.sNOME += c_sqls.ToString();
                    parametros_insert.Add(p);
                    c_parametros++;
                }
                k++;
            }

            this.sql.Append("INSERT INTO " + tabela + "(" + campos.Replace("#N#", String.Empty).Replace("@", String.Empty) + ") VALUES " + String.Join(",", vals));

            resultado = resultado && this.RunSQL(sql.ToString(), parametros_insert);

        }

        return resultado;
    }






public class ParamDbLIST : System.Collections.ObjectModel.Collection<ParamDB>
{/*I have other stuff here, but this will work*/}

   public class ParamDB
{
    public string sNOME { get; set; }
    public Object sVALOR { get; set; }}

记住方法 插入到 tbl_name (a,b,c) 价值观 (1,2,3), (4,5,6), (7,8,9); 每个命令限制为 1000 行。

我认为在这里做的一件好事是使用交易(为了安全)

你应该改变的方法是 RunSQL

如果这个方法可以优化,请告诉我

【讨论】:

    【解决方案4】:

    不确定 MySQL,但我发现到目前为止,使用 sql server 插入的最快方法类似于@Rahautos,但将值列表分隔到单独的查询中。 我知道这听起来很奇怪,但这将速度提高了 10 倍,从每秒 1200 次插入到 12000 次插入。 不知道有什么区别。 这笔交易也有很大帮助。

    INSERT INTO tbl_name
        (a,b,c)
    VALUES
        (1,2,3);
    INSERT INTO tbl_name
        (a,b,c);
    VALUES
        (4,5,6);
    INSERT INTO tbl_name
        (a,b,c);
    VALUES
        (7,8,9);
    

    示例代码

    using System;
    using System.Collections.Generic;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                var constring = (new SqlConnectionStringBuilder
                {
                    DataSource = "someserver",
                    InitialCatalog = "12trunk",
                    IntegratedSecurity = true
                }).ToString();
                using (var con = new SqlConnection(constring))
                {
                    con.Open();
                    using (var trans = con.BeginTransaction(isolationLevel: System.Data.IsolationLevel.ReadUncommitted) as SqlTransaction)
                    using (var cmd = new SqlCommand())
                    {
                        cmd.Transaction = trans;
                        cmd.Connection = con;
                        var start = DateTime.Now;
                        Console.WriteLine("Start = " + start);
                        const int inserts = 100000;
                        var builder = new StringBuilder();
                        cmd.CommandText = "delete from test";                    
                        for (int i = 0; i < inserts; i++)
                        {
                            Guid[] guids = new Guid[7];
                            for (int j = 0; j < 7; j++)
                            {
                                guids[j] = Guid.NewGuid();
                            }
                            var sql = $"insert into test (f0, f1, f2, f3, f4, f5, f6) values ('{guids[0]}', '{guids[1]}', '{guids[2]}','{guids[3]}', '{guids[4]}', '{guids[5]}', '{guids[6]}');\n";
                            builder.Append(sql);
                            if (i % 1000 == 0)
                            {
                                cmd.CommandText = builder.ToString();
                                cmd.ExecuteNonQuery();
                                builder.Clear();
                            }
    
                        }
                        cmd.CommandText = builder.ToString();
                        cmd.ExecuteNonQuery();
                        trans.Commit();
                        var ms = (DateTime.Now - start).TotalMilliseconds;
                        Console.WriteLine("Ms to run = " + ms);
                        Console.WriteLine("inserts per sec = " + inserts / (ms / 1000));
                        Console.ReadKey();
                    }
                }
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      我不确定 MYSQl。 但是在 MSSql Server 中我们使用 XML。 我们创建一个包含大量数据的 xml,然后在数据库中使用 Insert With Xml。

      INSERT INTO [TBL_USER_INFO](NAME, EMAIL)
         SELECT
            Result.value('value','varchar(25)') as NAME,
            Result.value('value','varchar(50)') as EMAIL
         FROM 
            @input.nodes('/EmployeList/Employee') AS ABCD(Result)
      

      【讨论】:

        猜你喜欢
        • 2016-02-06
        • 2021-10-30
        • 1970-01-01
        • 1970-01-01
        • 2012-01-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多