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

时间:2015-03-13 12:31:46

标签: 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)

然而,使用distinct(接受的答案)我得到:

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)

其中包含正确的附加数据。连接方法似乎也有效,因为测试数据稍慢。

3 个答案:

答案 0 :(得分:1)

为什么不使用窗口功能:

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

答案 1 :(得分:1)

你写道:

  

当然有一种更简单的方法,可以获得最后一个(最高)ID   每组使用最大聚合。但是,我并非如此   对id感兴趣但是与它相关的数据(即在...中)   同一行)。

此查询将为您提供与每个分拣机组的最高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

答案 2 :(得分:0)

选择不同的max(id)over(partition by sorter)id,sorter     来自2 asc的aggtest命令

返回: 5; 1 7; 2