【问题标题】:How to speed up query execution MySQL如何加快查询执行 MySQL
【发布时间】:2020-11-02 22:01:45
【问题描述】:

您必须填写表格表1至1,000,000:

Table view
CREATE TABLE `test` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , `value` INT UNSIGNED NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;"

我写了一个函数来做这个,但是向表中添加数据太慢了,如何提高插入数据的性能?

function InsertData(){

    global $MySQL;
    for($i = 1; $i != 1000000; $i++){
        $MySQL->query("INSERT INTO `name` (`id`, `value`) VALUES ($i, $i);");
    }

    $MySQL->close();
}

【问题讨论】:

    标签: php mysql sql sql-insert recursive-query


    【解决方案1】:

    您可以使用事务,以便每千次插入仅提交一次(或者,如果您有勇气,在数百万次查询之后)。这是(勇敢的)示例:

    function InsertData(){
       global $MySQL;
    
       // Start transactions
       $MySQL->query('SET autocommit=0;');
       $MySQL->query('START TRANSACTION;');
    
       for($i = 1; $i != 1000000; $i++){
          $MySQL->query("INSERT INTO `name` (`id`, `value`) VALUES ($i, $i);");
       }
       // So far, nothing as actually been saved to database
      
       // Commit all inserts.
       $MySQL->query('COMMIT;');
       $MySQL->query('SET autocommit=1;');
    
       $MySQL->close();
    }
    

    如果由于某些 MySQL 限制,这对于单个事务来说太多了,您可以每 10.000 次左右插入执行一次提交:

    function InsertData(){
       global $MySQL;
    
       // Start transactions
       $MySQL->query('SET autocommit=0;');
       $MySQL->query('START TRANSACTION;');
    
       for($i = 1; $i != 1000000; $i++){
          $MySQL->query("INSERT INTO `name` (`id`, `value`) VALUES ($i, $i);");
          if($i % 10000 == 0) {
             $MySQL->query('COMMIT;');
             $MySQL->query('START TRANSACTION;');
          }
       }
       // So far, nothing as actually been saved to database
      
       // Commit all inserts.
       $MySQL->query('COMMIT;');
       $MySQL->query('SET autocommit=1;');
    
       $MySQL->close();
    }
    

    注意最终限制 -> https://stackoverflow.com/a/2298325/2814721

    当然,这是一个实验或一次性脚本。不建议在生产数据库中进行。

    【讨论】:

      【解决方案2】:

      如何使用递归 CTE 将逻辑移至数据库?

      insert into name (id, value)
      with recursive cte as (
          select 1 id
          union all select id + 1 from cte where i < 1000000
      )
      select id, id from cte
      

      这可能是通过递归一次生成的多行。另一种方法是只生成 10 行,然后将这些行相乘:

      insert into name (id, value)
      with recursive cte as (
          select 0 id
          union all select id + 1 from cte where i < 9
      )
      select id, id 
      from (
          select 1 + c0.id + c1.id * 10 + c2.id * 100 + c3.id * 1000 + c4.id * 10000 + c5.id * 100000 id
          cte c0
          cross join cte c1
          cross join cte c2
          cross join cte c3
          cross join cte c4
          cross join cte c5
      ) t
      

      【讨论】:

      • 很遗憾,我无法使用 5 以上的 MySQL
      • 递归解决方案很好 - 它与我在下面发布的可变方法相当(递归稍微慢一点:8 秒与我的计算机上的 6.5 秒相比)。然而,第二个非常慢(1 分 18 秒)。
      【解决方案3】:

      试试这个查询:

      INSERT INTO `test` (`id`, `value`) 
      SELECT @row := @row + 1 AS row, @row 
      FROM (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS t1,
           (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS t2, 
           (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS t3, 
           (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS t4, 
           (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS t5, 
           (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS t6, 
           (SELECT @row:=0) AS nums;
      

      这是一种“插入到...它有效:

      1. 表 t1、t2、t3、t4、t5、t6 各有 10 行。交叉加入它们会生成 10^6 = 1000000 个组合,因此结果表将有百万行;
      2. 对于每一行,我们选择@row 变量两次。不仅如此,我们还增加了 1;
      3. nums表只用于一开始就将变量初始化为0;
      4. 将生成的表传递给 INSERT 语句,并将数据存储在表中。

      一个看起来更简洁的解决方案是将递归 CTE 与较新的 MySQL/MariaDB 一起使用。我是the one that was submitted by user GMB

      INSERT INTO test (id, value)
      WITH RECURSIVE temp AS (
          SELECT 1 AS row
          UNION SELECT row + 1 
          FROM temp
          WHERE row < 1000000
      )
      SELECT row, row
      FROM temp;
      

      根据我的测试,它有点慢。我没有监控内存使用情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-29
        • 2012-10-26
        • 1970-01-01
        相关资源
        最近更新 更多