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

标签 sql postgresql aggregate-functions greatest-n-per-group

我正在尝试获取存储在数据库表中的有序集的最后一个元素。顺序由表中的一列定义。此外,该表包含多个集合,因此我想要每个集合的最后一个。

作为示例,请考虑下表:

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 $2;
$$;

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)

除了排序器之外,按 id 分组显然也不起作用。

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

实现这一目标的最佳方法是什么?

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

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

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)

其中包含正确的附加数据。连接方法似乎也有效,但在测试数据上稍微慢一些。

最佳答案

为什么不使用窗口函数:

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

关于sql - 获取 postgresql 中有序集合的最后一个元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29032345/

相关文章:

sql - 如何在 PostgreSql 的预订表中找到第一个空闲时间

sql - 在 PostgreSQL 中显示正在运行的查询的全文

sql - Postgresql 使用 like 选择数组重叠的位置

sql - postgresql 中的第一个和最后一个值聚合函数可以正确处理 NULL 值

postgresql - 优化 postgres View 的时间戳和来自另一个表的字段聚合

sql - ActiveRecord 的 "order"方法是否容易受到 SQL 注入(inject)?

sql - 设置变量时,记录的 IS NOT NULL 测试不返回 TRUE

sql - 对 varchar 列建立索引

java - JPA查询返回实体列表

aggregate-functions - 如何聚合/汇总百分位测量