如何在非常大的表中以最有效的方式使用 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
)
我应该写一个脚本:
- 将记录从
purchase
移动到 purchase_archive
table
- 对于每个移动的行
users_balance
应该更新(如果他是买家,用户余额应该减少 amount
,如果他是卖家,用户余额应该增加相同的金额)
解决这个任务的最佳方法是什么? (PHP + Mysql PDO)
我的假设是:
- 将 table 引擎设置为 InnoDB
- SELECT 第一批 1000 行 table
- 开始事务(这就是 InnoDB 的原因)
每行
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);
使用这样的查询将行移动到存档中:
INSERT INTO `purchase_archive` SELECT * FROM `purchase` WHERE `id` IN (".$temp.");
DELETE QUICK FROM `transactions` WHERE `id` IN (".$temp.");
结束交易
并循环重复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)
我有一个tablepurchase
类似 (id
, buyer_id
, seller_id
, amount
) 的东西有 20-30 百万条记录
我还有一个 table 具有相同结构 purchase_archive
和 table users_balance
(id
, user_id
, balance
)
我应该写一个脚本:
- 将记录从
purchase
移动到purchase_archive
table - 对于每个移动的行
users_balance
应该更新(如果他是买家,用户余额应该减少amount
,如果他是卖家,用户余额应该增加相同的金额)
解决这个任务的最佳方法是什么? (PHP + Mysql PDO)
我的假设是:
- 将 table 引擎设置为 InnoDB
- SELECT 第一批 1000 行 table
- 开始事务(这就是 InnoDB 的原因)
每行
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);
使用这样的查询将行移动到存档中:
INSERT INTO `purchase_archive` SELECT * FROM `purchase` WHERE `id` IN (".$temp."); DELETE QUICK FROM `transactions` WHERE `id` IN (".$temp.");
结束交易
并循环重复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)