在postgresql中获取有序集合的最后一个元素

Get last element of an ordered set in postgresql

我正在尝试获取存储在数据库中的有序集合的最后一个元素 table。排序由 table 中的列之一定义。另外 table 包含多个集合,所以我想要每个集合的最后一个集合。

例如考虑以下 table:

benchmarks=# select id,sorter from aggtest ;
 id | sorter 
----+--------
  1 |      1
  3 |      1
  5 |      1
  2 |      2
  7 |      2
  4 |      1
  6 |      2
(7 rows)

排序器 1 和 2 定义每个集合,集合按 id 列排序。为了获取每个集合的最后一个元素,我定义了一个聚合函数:

CREATE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT ;
$$;

CREATE AGGREGATE public.last (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

正如解释的那样here

然而,当我使用它时,我得到:

benchmarks=# select last(id),sorter from aggtest group by sorter order by sorter;
 last | sorter 
------+--------
    4 |      1
    6 |      2
(2 rows)

但是,我想获得 (5,1)(7,2),因为它们是集合中的最后一个 ID(数字)。查看聚合机制的工作原理,我可以很好地理解为什么结果不是我想要的。这些项目按照我添加它们的顺序返回,然后聚合以便返回我添加的最后一个。

我试过按 id 排序,这样每个组都是独立排序的,但这不起作用:

benchmarks=# select last(id),sorter from aggtest group by sorter order by sorter,id;
ERROR:  column "aggtest.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: ...(id),sorter from aggtest group by sorter order by sorter,id;

如果我将排序条件包装在另一个聚合中,我又会得到错误的数据:

benchmarks=# select last(id),sorter from aggtest group by sorter order by sorter,last(id);
 last | sorter 
------+--------
    4 |      1
    6 |      2
(2 rows)

除了 sorter 之外还按 id 分组显然不起作用。

当然有一种更简单的方法,即使用 max 聚合获取每个组的最后(最高)ID。但是,我对 id 不太感兴趣,而是对与其关联的数据(即在同一行)感兴趣。因此,我不按 id 排序然后聚合,以便为每个组返回具有最高 id 的行。

完成此任务的最佳方法是什么?

编辑:为什么按排序器分组的 max(id) 不起作用

假设以下完整table(unsorter代表我在table中的附加数据):

benchmarks=# select * from aggtest ;
 id | sorter | unsorter 
----+--------+----------
  1 |      1 |        1
  3 |      1 |        2
  5 |      1 |        3
  2 |      2 |        4
  7 |      2 |        5
  4 |      1 |        6
  6 |      2 |        7
(7 rows)

我想检索行:

 id | sorter | unsorter 
----+--------+----------
  5 |      1 |        3
  7 |      2 |        5

但是使用 max(id) 并按排序器分组我得到:

benchmarks=# select max(id),sorter,unsorter from aggtest group by sorter;
ERROR:  column "aggtest.unsorter" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: select max(id),sorter,unsorter from aggtest group by sorter;

使用 max(unsorter) 显然也不起作用:

benchmarks=# select max(id),sorter,max(unsorter) from aggtest group by sorter;
 max | sorter | max 
-----+--------+-----
   5 |      1 |   6
   7 |      2 |   7
(2 rows)

但是使用不同的(接受的答案)我得到:

benchmarks=# select distinct on (sorter) id,sorter,unsorter from aggtest order by sorter, id desc;
 id | sorter | unsorter 
----+--------+----------
  5 |      1 |        3
  7 |      2 |        5
(2 rows)

其中有正确的附加数据。 join 方法似乎也有效,在测试数据上 by 稍微慢一点。

为什么不使用 window 函数:

select id, sorter
from (
   select id, sorter, 
          row_number() over (partition by sorter order by id desc) as rn
   from aggtest
) t
where rn = 1;

或者使用通常更快的 Postgres distinct on 运算符:

select distinct on (sorter) id, sorter
from aggtest
order by sorter, id desc

你写:

Of course there is an easier way, to get the last (highest) id for each group by using the max aggregate. However, I am not so much interested in the id but as in data associated with it (i.e. in the same row).

此查询将为您提供与每个排序器组的最高 ID 关联的数据。

select a.* from aggtest a
join (
    select max(id) max_id, sorter 
    from aggtest
    group by sorter
) b on a.id = b.max_id and a.sorter = b.sorter

select distinct max(id) over (partition by sorter) id,sorter 从 aggtest order by 2 asc

returns: 5;1 7;2