我有两个表 - 帐户(银行帐户)和交易。
账号:
CREATE TABLE "Account"
(
id SERIAL
PRIMARY KEY,
name TEXT NOT NULL,
"clientId" INTEGER NOT NULL
REFERENCES "Client"
ON UPDATE CASCADE ON DELETE RESTRICT,
currency "Currency" NOT NULL,
balance DOUBLE PRECISION DEFAULT 0.0 NOT NULL,
deleted BOOLEAN DEFAULT FALSE NOT NULL,
"createdAt" TIMESTAMP(3) DEFAULT current_timestamp NOT NULL,
);
交易:
CREATE TABLE "Transaction"
(
id SERIAL
PRIMARY KEY,
type "TransactionType" NOT NULL,
"fromAccountId" INTEGER
REFERENCES "Account"
ON UPDATE CASCADE ON DELETE SET NULL,
"fromAmount" DOUBLE PRECISION,
"toAccountId" INTEGER
REFERENCES "Account"
ON UPDATE CASCADE ON DELETE SET NULL,
"toAmount" DOUBLE PRECISION,
"createdAt" TIMESTAMP(3) DEFAULT current_timestamp NOT NULL,
status "TransactionStatus" NOT NULL
);
fromAccountId
- 提款账户的 ID toAccountId
- 资金将进入的账户 ID fromAmount
- 将从 fromAccountId
toAmount
- 将存入 fromAccountId
在所有交易类型中,有一种
EXCHANGE
类型,即从一种货币账户到另一种货币账户的转账。
我面临着统计某些时期的用户余额的任务。这只能通过遍历并累加该期间的所有交易并将所有相应的值相加来完成。
为了测试,我编写了一个 SQL 查询来检查交换交易的值是否计算正确(列
ingoing_exchange
和 outgoing_exchange
)
SELECT a.id AS account_id,
a.currency,
sum(coalesce(t."toAmount", 0)) - sum(coalesce(t."fromAmount", 0)) AS balance,
ingoing_exchange_transactions."toAmount" AS ingoing_exchange,
outgoing_exchange_transactions."fromAmount" AS outgoing_exchange,
date_part('month', date_trunc('month', t."createdAt")) AS date,
date_part('year', date_trunc('year', t."createdAt")) AS year
FROM "Transaction" t
INNER JOIN "Account" a ON (t."fromAccountId" = a.id OR t."toAccountId" = a.id)
/* JOIN Exchange транзакций */
LEFT JOIN LATERAL (SELECT "toAccountId", "toAmount"
FROM "Transaction"
WHERE "type" = 'EXCHANGE'
AND (t.status = 'COMPLETE' OR t.status = 'PENDING')) ingoing_exchange_transactions
ON a.id = ingoing_exchange_transactions."toAccountId"
LEFT JOIN LATERAL (SELECT "fromAccountId", "fromAmount"
FROM "Transaction"
WHERE "type" = 'EXCHANGE'
AND (t.status = 'COMPLETE' OR t.status = 'PENDING')) outgoing_exchange_transactions
ON a.id = outgoing_exchange_transactions."fromAccountId"
WHERE t.type <> 'EXCHANGE'
AND (t.status = 'COMPLETE' OR t.status = 'PENDING')
GROUP BY account_id, a.currency, ingoing_exchange, outgoing_exchange, date, year
ORDER BY date, year;
这段代码确实正确地计算了所有内容。例如,如果您转到交易表并查看
toAmount = 208
的交易,那么这两个数字确实会在那里
但是,我们对这个结果并不满意;我们需要计算每个
ingoing_exchange
的总outgoing_exchange
和account_id
,然后用balance
列添加和减去相应的值。
但是,如果您还没有向余额添加任何内容,只是简单地求和以检查总和是否正确,那么 Postgres 返回的金额会比实际金额大 10 倍
SELECT a.id AS account_id,
a.currency,
sum(coalesce(t."toAmount", 0)) - sum(coalesce(t."fromAmount", 0)) AS balance,
sum(ingoing_exchange_transactions."toAmount") AS ingoing_exchange,
sum(outgoing_exchange_transactions."fromAmount") AS outgoing_exchange,
date_part('month', date_trunc('month', t."createdAt")) AS date,
date_part('year', date_trunc('year', t."createdAt")) AS year
FROM "Transaction" t
INNER JOIN "Account" a ON (t."fromAccountId" = a.id OR t."toAccountId" = a.id)
LEFT JOIN LATERAL (SELECT "toAccountId", "toAmount"
FROM "Transaction"
WHERE "type" = 'EXCHANGE'
AND (t.status = 'COMPLETE' OR t.status = 'PENDING')) ingoing_exchange_transactions
ON a.id = ingoing_exchange_transactions."toAccountId"
LEFT JOIN LATERAL (SELECT "fromAccountId", "fromAmount"
FROM "Transaction"
WHERE "type" = 'EXCHANGE'
AND (t.status = 'COMPLETE' OR t.status = 'PENDING')) outgoing_exchange_transactions
ON a.id = outgoing_exchange_transactions."fromAccountId"
WHERE t.type <> 'EXCHANGE'
AND (t.status = 'COMPLETE' OR t.status = 'PENDING')
GROUP BY account_id, a.currency, date, year
ORDER BY date, year;
现在
ingoing_exchange
列的 account_id = 208
显然不包含等于 48835+77750
的值。 outgoing_exchange
也不正确。 为什么会发生这种情况?还有一件事:是否有可能以某种方式改进这个查询?
我预计至少在
126585
的 ingoing_exchange
和 outgoing_exchange
列中获得 account_id = 208
。
第一个查询按
ingoing_exchange
(以及其他)分组。可能有多行具有相同的“分组依据”值。
第二个查询总结了所有
ingoing_exchange
。任何重复项都会添加到此处的总和中。
→ 将
count(*)
添加到第一个查询中,以查看每行折叠了多少个重复项。
另外,您还有
ORDER BY date, year
并显示一页 结果行,所有这些都具有相同的 (date, year)
。显然还有更多的结果行,其中包含同一组的其他行。
→ 将第一个查询的排序顺序更改为
ORDER BY account_id, date, year
,以实际看到同一 account_id 的所有行紧挨着。