为什么 SUM 返回值远大于实际值?

问题描述 投票:0回答:1

我有两个表 - 帐户(银行帐户)和交易。

账号:

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

sql database postgresql
1个回答
0
投票

第一个查询按

ingoing_exchange
(以及其他)分组。可能有多行具有相同的“分组依据”值。

第二个查询总结了所有

ingoing_exchange
。任何重复项都会添加到此处的总和中。

→ 将

count(*)
添加到第一个查询中,以查看每行折叠了多少个重复项。

另外,您还有

ORDER BY date, year
并显示一页 结果行,所有这些都具有相同的
(date, year)
。显然还有更多的结果行,其中包含同一组的其他行。

→ 将第一个查询的排序顺序更改为

ORDER BY account_id, date, year
,以实际看到同一 account_id 的所有行紧挨着。

© www.soinside.com 2019 - 2024. All rights reserved.