我需要为每个用户显示余额,与其他用户相关。
表结构&虚拟数据脚本:
CREATE TABLE transactions (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user1 INT NOT NULL,
user2 INT NOT NULL,
amount INT NOT NULL
);
INSERT INTO transactions VALUES(1, 1, 2, 10);
INSERT INTO transactions VALUES(2, 1, 3, 15);
INSERT INTO transactions VALUES(3, 4, 1, 25);
INSERT INTO transactions VALUES(4, 1, 5, 20);
INSERT INTO transactions VALUES(5, 5, 1, 18);
INSERT INTO transactions VALUES(6, 5, 1, 2);
结果:
现在我想总结一下user = 1
的信息(余额)。我希望看到的结果是:
user balance
2 10
3 15
4 -25
5 0
现在,我正在使用最新的稳定MySQL版本5.7.17-0ubuntu0.16.04.1
。
我有两个问题:
FULL OUTER JOIN
子句WITH
子句此时我的双手被束缚住了。有人可以通过fast
和efficient
查询上述情况来帮助我吗?
以下是我的两次尝试(无效):
这个没有用,因为我不能使用FULL OUTER JOIN
子句
SELECT IFNULL(t3.user, t4.user), IFNULL(t3.amount, 0) - IFNULL(t4.amount, 0)
FROM (
select t1.user2 user, sum(t1.amount) amount
from transactions t1
where 1=1
and t1.user1 = 1
group by t1.user2
) t3
FULL OUTER JOIN (
select t2.user1 user, sum(t2.amount) amount
from transactions t2
where 1=1
and t2.user2 = 1
group by t2.user1
) t4 ON t3.user = t4.user
这个没有用,因为我不能使用WITH
子句
WITH t3 AS
(
select t1.user2 user, sum(t1.amount) amount
from transactions t1
where 1=1
and t1.user1 = 1
group by t1.user2
),
t4 AS
(
select t2.user1 user, sum(t2.amount) amount
from transactions t2
where 1=1
and t2.user2 = 1
group by t2.user1
)
SELECT
t1.user,
IFNULL(t3.amount, 0) - IFNULL(t4.amount, 0) balance
FROM t1
LEFT JOIN t3 ON t1.user = t2.user
UNION
SELECT t2.user FROM t1
RIGHT JOIN t3 ON t1.user = t2.user
更新
使用 Gurwinder Singh 提供的解决方案,我能够测试大约5百万行测试数据的两个查询的性能(尽管user1 = 1或user2 = 1的数据数量) - 远不止于此。)
和(与工会)
相应。查询1 快34%((3.4-2.24)/3.4*100 = 34)。
请注意,此表中没有索引。我稍后会尝试使用MariaDB进行相同类型的测试并比较结果。
更新2
为列编制索引后:user1
,user2
,amount
情况已发生变化。
查询1运行时间:
显示0到2行(总共3行,查询耗时1.9857秒。)
查询2运行时间:
显示0到2行(总共3行,查询耗时1.5641秒。)
但我仍然认为这是非常糟糕的结果。也许我会设置一些触发器来将余额更新为专用表。但在这一点上,答案得到了回答。
答案 0 :(得分:1)
您可以使用基于CASE
的条件聚合:
试试这个:
select case
when user1 = 1
then user2
else user1
end as user,
sum(case
when user1 = 1
then amount
else - amount
end) as amount
from transactions
where 1 in (user1, user2)
group by case
when user1 = 1
then user2
else user1
end;
或两步聚合:
select user, sum(amount) as amount
from (
select user2 as user, sum(amount) as amount
from transactions
where user1 = 1
group by user2
union all
select user1 as user, -sum(amount) as amount
from transactions
where user2 = 1
group by user1
) t
group by user;