在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
我正在尝试获取存储在数据库中的有序集合的最后一个元素 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