无法按非 ID 列执行 Postgres 组以获取包含最大值的 ID

Trouble performing Postgres group by non-ID column to get ID containing max value

我正在尝试对联接执行 GROUP BY table table。联接 table 基本上看起来像:

CREATE TABLE user_foos (
    id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    foo_id INT NOT NULL,
    effective_at DATETIME NOT NULL
);
ALTER TABLE user_foos
    ADD CONSTRAINT user_foos_uniqueness
    UNIQUE (user_id, foo_id, effective_at);

我想查询此 table 以查找 effective_at 是给定的任何一对 user_id, foo_id 的最大值的所有记录。我试过以下方法:

SELECT "user_foos"."id",
       "user_foos"."user_id",
       "user_foos"."foo_id",
       max("user_foos"."effective_at")
FROM "user_foos"
GROUP BY "user_foos"."user_id", "user_foos"."foo_id";

不幸的是,这会导致错误:

column "user_foos.id" must appear in the GROUP BY clause or be used in an aggregate function

我知道问题与 "id" 未在聚合函数中使用有关,并且如果数据库发现多个具有不同 ID 的记录,它不知道该怎么做,但我知道这永远不可能由于我在这些列中的三元主键(user_idfoo_ideffective_at)而发生这种情况。

为了解决这个问题,我还尝试了一些其他变体,例如在 id 上使用 first_value window function

SELECT first_value("user_foos"."id"),
       "user_foos"."user_id",
       "user_foos"."foo_id",
       max("user_foos"."effective_at")
FROM "user_foos"
GROUP BY "user_foos"."user_id", "user_foos"."foo_id";

和:

SELECT first_value("user_foos"."id")
FROM "user_foos"
GROUP BY "user_foos"."user_id", "user_foos"."foo_id"
HAVING "user_foos"."effective_at" = max("user_foos"."effective_at")

不幸的是,这两者都会导致不同的错误:

window function call requires an OVER clause

理想情况下,我的目标是获取所有匹配的 id,这样我就可以在子查询中使用它从这个 table 中获取合法的完整行数据以匹配记录。任何人都可以提供有关如何使它正常工作的见解吗?

尝试:

SELECT *
FROM (
  SELECT t.*,
         row_number() OVER( partition by user_id, foo_id ORDER BY effective_at DESC ) x
  FROM user_foos t
)
WHERE x = 1

如果您不想使用基于所有三个键的组合的子查询,那么您需要创建一个 "dense rank" window 函数字段来排序 id 的子集,user_id 和 foo_id 按生效日期与排名顺序字段。然后对其进行子查询并获取 rank_order=1 的记录。由于排名排序是按生效日期进行的,因此您将获得每个 foo 和用户具有最高生效日期的记录的所有字段。

DATSET
1 1 1 01/01/2001
2 1 1 01/01/2002
3 1 1 01/01/2003
4 1 2 01/01/2001
5 2 1 01/01/2001

DATSET WITH RANK ORDER PARTITIONED BY FOO_ID, USER_ID ORDERED BY DATE DESC
1 3 1 1 01/01/2001
2 2 1 1 01/01/2002
3 1 1 1 01/01/2003
4 1 1 2 01/01/2001
5 1 2 1 01/01/2001

SELECT * FROM QUERY ABOVE WHERE RANK_ORDER=1
3 1 1 1 01/01/2003
4 1 1 2 01/01/2001
5 1 2 1 01/01/2001

Postgres 有一个非常好的特性叫做distinct on,在这种情况下可以使用它:

SELECT DISTINCT ON (uf."user_id", uf."foo_id") uf.*
FROM "user_foos" uf
ORDER BY uf."user_id", uf."foo_id", uf."effective_at" DESC;

它 returns 组中的第一行,基于括号中的值。 order by 子句需要包含这些值以及第三列以确定哪一行是组中的第一行。