如何在非常大的表中以最有效的方式使用 SQL

How to use SQL in the most efficient way with very big tables

我有一个tablepurchase 类似 (id, buyer_id, seller_id, amount) 的东西有 20-30 百万条记录 我还有一个 table 具有相同结构 purchase_archive 和 table users_balance (id, user_id, balance)

我应该写一个脚本:

  1. 将记录从 purchase 移动到 purchase_archive table
  2. 对于每个移动的行 users_balance 应该更新(如果他是买家,用户余额应该减少 amount,如果他是卖家,用户余额应该增加相同的金额)

解决这个任务的最佳方法是什么? (PHP + Mysql PDO)

我的假设是:

  1. 将 table 引擎设置为 InnoDB
  2. SELECT 第一批 1000 行 table
  3. 开始事务(这就是 InnoDB 的原因)
  4. 每行

    4.1 将 id 存储在数组中 ($temp)

    4.2 使用查询更新余额

    SELECT `amount` FROM `purchase` WHERE `id` = :tid LIMIT 1 INTO @amount;
    UPDATE `users_balance` SET `balance` = CASE 
    WHEN `user_id` = :seller_id THEN `balance` + @amount 
    WHEN `user_id` = :buyer_id THEN `balance` - @amount END 
    WHERE `user_id` IN (:buyer_id, :seller_id);
    
  5. 使用这样的查询将行移动到存档中:

        INSERT INTO `purchase_archive` SELECT * FROM `purchase` WHERE `id` IN (".$temp.");
        DELETE QUICK FROM `transactions` WHERE `id` IN (".$temp.");
    
  6. 结束交易

并循环重复2-6

最长的操作是4.2点,我不知道没有变量如何更快地执行它

有没有更快的方法?

P.S。对不起我糟糕的英语。

您可以尝试这样的操作:

update user_balance b 
  inner join (
    select b.user_id, 
           sum(case when p.buyer_id = b.user_id then p.amount else 0 end) bought, 
           sum(case when p.seller_id = b.user_id then p.amount else 0 end) sold
    from purchase p 
      inner join user_balance b 
        on p.buyer_id = b.user_id 
          or p.seller_id = b.user_id 
    group by b.user_id) q 
    on b.user_id = q.user_id 
    set b.amount = b.amount + q.sold - q.bought;

它应该在一个查询中完成所有事情。如果您愿意,可以在内部查询中进一步限制范围。 SQL Fiddle 好像挂了所以不能提供live demo,不过有这个:

mysql> select * from user_balance;
+---------+--------+
| user_id | amount |
+---------+--------+
|       1 |     50 |
|       2 |     50 |
|       3 |     50 |
|       4 |     50 |
+---------+--------+
4 rows in set (0.00 sec)

mysql> select * from purchase;
+-------------+-----------+----------+--------+
| purchase_id | seller_id | buyer_id | amount |
+-------------+-----------+----------+--------+
|           1 |         1 |        2 |     10 |
|           2 |         3 |        4 |     20 |
|           3 |         4 |        2 |      5 |
|           4 |         1 |        4 |      7 |
|           5 |         3 |        1 |      9 |
+-------------+-----------+----------+--------+
5 rows in set (0.00 sec)

查询后....

mysql> update user_balance b inner join (select b.user_id, sum(case when p.buyer_id = b.user_id then p.amount else 0 end) bought, sum(case when p.seller_id = b.user_id then p.amount else 0 end) sold from purchase p inner join user_balance b on p.buyer_id = b.user_id or p.seller_id = b.user_id group by b.user_id) q on b.user_id = q.user_id set b.amount = b.amount + q.sold - q.bought;
Query OK, 4 rows affected (0.07 sec)
Rows matched: 4  Changed: 4  Warnings: 0

mysql> select * from user_balance;
+---------+--------+
| user_id | amount |
+---------+--------+
|       1 |     58 |
|       2 |     35 |
|       3 |     79 |
|       4 |     28 |
+---------+--------+
4 rows in set (0.00 sec)