我有一个具有以下结构的MySQL
transfers
表:
CREATE TABLE `transfers` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`id_account_destination` bigint(20) unsigned NOT NULL,
`id_account_origin` bigint(20) unsigned NOT NULL,
`amount` decimal(10, 2) signed NOT NULL DEFAULT 0,
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `id_account_destination` (`id_account_destination`),
KEY `id_account_origin` (`id_account_origin`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
显然,这张表存储转账。它记录了transferamount和涉及的两个account:origin和destination。
我需要的是创建一个
SELECT
报表,该报表从一个时期(由 created_at
过滤)获取所有给定的 transfers,并显示所有当前账户在该部分中有多少收入和结果。像这样的东西:
+------------+---------------+-----------------|
| id_account | income_amount | outcome_ammount |
+------------+---------------+-----------------|
| 19 | 27690.87 | 57204.80 |
| 112 | 1000.00 | 2349.00 |
| 1011 | 575877.56 | 17454.50 |
| 17 | 135002.61 | 204.30 |
+------------+---------------+-----------------|
这里的难点在于
id_account
列将从任何传输中获取id_account_origin
或id_account_destination
并通过其余传输找到所有其他事件。如果在“origin”位置找到账户,转账的amount会添加到outcome_ammount
栏;但如果在“目标”位置找到该帐户,则应将转账的amount添加到income_ammount
列。
所以输出表与传输表完全不同,尽管所有需要的信息都已经存在了。
到目前为止,我所取得的成就是仅以一种方式获取该信息:
SELECT
id_account_origin,
SUM(t.amount) AS outcome_amount
FROM transfers t
GROUP BY t.id_account_origin;
哪个返回:
+-------------------+----------------+
| id_account_origin | outcome_amount |
+-------------------+----------------+
| 10009 | 2761390.87 |
| 10012 | 1000.00 |
| 10011 | 575877.56 |
| 10007 | 135002.61 |
+-------------------+----------------+
而且比上面预期的交叉列要容易得多。
为
id_account_destination
写另一个查询。然后将它们与 UNION
结合起来以取消旋转。
SELECT id_account, SUM(outcome_amount) AS outcome_amount, SUM(income_amount) AS income_amount
FROM (
SELECT id_account_origin AS id_account, SUM(t.amount) AS outcome_amount, 0 AS income_amount
FROM transfers t
GROUP BY id_account
UNION ALL
SELECT id_account_destination AS id_account, 0 AS outcome_amount, SUM(-t.amount) AS income_amount
FROM transfers t
GROUP BY id_account
) AS x
GROUP BY id_account
一种方法是反透视数据,然后聚合。
在最近的 MySQL 版本中,我们可以使用横向连接,这避免了两次扫描表
union all
:
select x.id_account,
sum(x.income_amount) income_amount,
sum(x.outcome_amount) outcome_amount
from transfers t
cross join lateral (
select t.id_account_destination, t.amount, 0
union all select t.id_account_origin, 0, t.amount
) x(id_account, income_amount, outcome_amount)
group by x.id_account
这里是一个小演示.
样本数据:
id | id_account_destination | id_account_origin | 数量 | 创建于 |
---|---|---|---|---|
1 | 1 | 2 | 100.00 | 2023-05-04 19:59:37 |
2 | 1 | 3 | 50.00 | 2023-05-04 19:59:37 |
3 | 4 | 1 | 30.00 | 2023-05-04 19:59:37 |
结果:
id_account | 收入_金额 | 结果_金额 |
---|---|---|
1 | 150.00 | 30.00 |
2 | 0.00 | 100.00 |
3 | 0.00 | 50.00 |
4 | 30.00 | 0.00 |