SELECT 列表不在 GROUP BY 子句中并且包含非聚合列,该列在功能上不依赖于 GROUP BY 子句中的列

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

在金融应用程序的数据库中,有如下表格:

  • account(account_id PK, account_name, …)
    ,
  • transaction(transaction_id PK, transaction_timestamp, account_debit_id FK, account_credit_id FK)
    ,和
  • balance_event(account_balance, account_id FK, transaction_id FK)
    .

SQL 定义如下。仅列出相关字段。

后者用于跟踪每个账户的余额;命名可能很糟糕,但该表记录了由于某些交易导致帐户余额发生变化时的事件。这种方式获取账户余额不需要遍历和汇总所有的数千笔交易,而且账户余额的历史记录也被保留以备将来参考。

我想描述一个查询1 ,它给出了

account
表中的所有行,以及从
account_balance
 中获取的相应 
balance_event
,——一个经典问题。我尝试了很多不同的方法,但由于不同的原因,没有一种方法有效(另外,ChatGPT 有很多变体,但我不在这里包括它们)。有些查询不起作用,因为
ONLY_FULL_GROUP_BY
已打开;如果可能的话,我想保持这种状态。

这大概就是我想要做的:

-- I know this query doesn't make any sense, don't bite me
-- This is only a gist, a suggestion
SELECT
  account.*,
  balance_event.account_balance
FROM
  account
JOIN
  balance_event ON account.account_id = balance_event.account_id
WHERE
  balance_event.transaction_id = (
    SELECT
      `transaction`.transaction_id
    FROM
      `transaction`
    JOIN
      balance_event ON balance_event.transaction_id = `transaction`.transaction_id
    WHERE
      balance_event.account_id = account.account_id
      AND
      `transaction`.transaction_timestamp = MAX(`transaction`.transaction_timestamp)
    GROUP BY
      balance_event.account_id
  )
GROUP BY
  balance_event.account_id
;

如何正确地做,以便它真正起作用?


1 — 我正在随机在线 SQL 编辑器中测试代码,该编辑器恰好使用 MySQL,但我寻求有一个通用查询或至少一个 PostgreSQL 支持的查询。


供参考,这是字段的完整定义:

CREATE TABLE account (
  account_id VARCHAR(36) NOT NULL PRIMARY KEY, -- uuid
  account_name VARCHAR(255) NOT NULL
);

CREATE TABLE `transaction` (
  transaction_id VARCHAR(36) NOT NULL PRIMARY KEY, -- uuid
  transaction_timestamp BIGINT NOT NULL,
  account_debit_id VARCHAR(36) NOT NULL,
  account_credit_id VARCHAR(36) NOT NULL,
  transaction_amount DECIMAL(10,2) NOT NULL,
  FOREIGN KEY (account_debit_id) REFERENCES account(account_id),
  FOREIGN KEY (account_credit_id) REFERENCES account(account_id)
);

CREATE TABLE balance_event (
  balance_event_id VARCHAR(36) NOT NULL PRIMARY KEY, -- uuid
  transaction_id VARCHAR(36) NOT NULL,
  account_id VARCHAR(36) NOT NULL,
  account_balance DECIMAL(10,2) NOT NULL,
  FOREIGN KEY (transaction_id) REFERENCES `transaction`(transaction_id),
  FOREIGN KEY (account_id) REFERENCES account(account_id)
);

mysql sql postgresql greatest-n-per-group
1个回答
0
投票

这是一个使用 Window Functions 的解决方案,PostgreSQLMySQL 8.0 以及大多数其他流行的 SQL 数据库,包括商业和开源。

SELECT *
FROM (
  SELECT
    a.*,
    b.account_balance,
    ROW_NUMBER() OVER (PARTITION BY a.account_id ORDER BY t.transaction_timestamp DESC) AS rownum
  FROM account AS a
  JOIN balance_event AS b USING (account_id)
  JOIN transaction AS t USING (transaction_id)
) t
WHERE rownum = 1;

有了这个方案,你根本不需要GROUP BY,所以MySQL的ONLY_FULL_GROUP_BY SQL模式不会影响它。

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