Postgresql Postgres聚合函数,用于选择组中的最佳/第一个元素
我有一个元素表,其中有许多项需要根据优先级进行重复数据消除。以下是一个非常简单但具有代表性的示例:Postgresql Postgres聚合函数,用于选择组中的最佳/第一个元素,postgresql,aggregate-functions,Postgresql,Aggregate Functions,我有一个元素表,其中有许多项需要根据优先级进行重复数据消除。以下是一个非常简单但具有代表性的示例: sophia=> select * from numbers order by value, priority; value | priority | label -------+----------+------- 1 | 1 | One 1 | 2 | Eins 2 | 1 | Two 2 |
sophia=> select * from numbers order by value, priority;
value | priority | label
-------+----------+-------
1 | 1 | One
1 | 2 | Eins
2 | 1 | Two
2 | 2 | Zwei
3 | 2 | Drei
4 | 1 | Four
4 | 2 | Vier
(7 rows)
我想将此限制为每个数字只返回一行。非常简单,我可以使用中详细介绍的第一个()聚合函数
问题是顺序没有很好地定义,因此如果以不同的顺序插入DB行,我可能会得到以下结果:
sophia=> select value, first(label) from numbers group by value order by value;
value | first
-------+-------
1 | Eins
2 | Zwei
3 | Drei
4 | Vier
(4 rows)
当然,解决这个问题的方法似乎也很简单,因为我可以通过以下方式进行排序:
sophia=> select value, first(label) from (select * from numbers order by priority) foo group by value order by value;
value | first
-------+-------
1 | One
2 | Two
3 | Drei
4 | Four
(4 rows)
sophia=>
然而,这里的问题是查询优化器可以自由地丢弃子查询中的order by规则,这意味着这并不总是有效的,并且会在随机的地方中断
我有一个解决方案,我目前正在少数几个地方使用,它依赖于array_agg
sophia=> select value, (array_agg(label order by priority))[1] as best_label from numbers group by value;
value | best_label
-------+------------
1 | One
2 | Two
3 | Drei
4 | Four
(4 rows)
sophia=>
这提供了健壮的排序,但需要在查询时创建一堆额外的数组,这些数组会被丢弃,因此在更大的数据集上的性能相当糟糕
所以问题是,有没有更好、更干净、更快的方法来处理这个问题?你上次的尝试包括了你问题的答案,但你没有意识到:
array_agg(label order by priority)
请注意聚合函数中的orderby
子句。这不是array\u agg
的特殊功能,而是以下内容的一般部分:
通常,输入行以未指定的顺序馈送到聚合函数。在许多情况下,这并不重要;例如,min无论以何种顺序接收输入,都会产生相同的结果。但是,某些聚合函数(如array_agg和string_agg)生成的结果取决于输入行的顺序。使用这种聚合时,可以使用可选的order_by_子句指定所需的顺序。order_by_子句的语法与查询级order by子句的语法相同,如第7.5节所述,只是其表达式始终只是表达式,不能输出列名或数字
因此,您的问题的解决方案就是在第一个
聚合表达式中放入一个order by
:
select value, first(label order by priority) from numbers group by value order by value;
考虑到这是多么优雅,我很惊讶
first
和last
仍然没有作为内置聚合实现。您上次的尝试包括了您问题的答案,您只是没有意识到:
array_agg(label order by priority)
请注意聚合函数中的orderby
子句。这不是array\u agg
的特殊功能,而是以下内容的一般部分:
通常,输入行以未指定的顺序馈送到聚合函数。在许多情况下,这并不重要;例如,min无论以何种顺序接收输入,都会产生相同的结果。但是,某些聚合函数(如array_agg和string_agg)生成的结果取决于输入行的顺序。使用这种聚合时,可以使用可选的order_by_子句指定所需的顺序。order_by_子句的语法与查询级order by子句的语法相同,如第7.5节所述,只是其表达式始终只是表达式,不能输出列名或数字
因此,您的问题的解决方案就是在第一个
聚合表达式中放入一个order by
:
select value, first(label order by priority) from numbers group by value order by value;
考虑到这是多么优雅,我很惊讶
first
和last
仍然没有作为内置聚合实现。Postgres select语句有一个名为DISTINCT ON的子句,当您想要返回组中的一个时,该子句非常有用。在这种情况下,您将使用:
SELECT DISTINCT ON (value) value, label
FROM numbers
ORDER BY value, priority;
使用DISTINCT ON通常比涉及组或窗口函数的其他方法更快。Postgres select语句有一个名为DISTINCT ON的子句,当您想要返回组中的一个时,该子句非常有用。在这种情况下,您将使用:
SELECT DISTINCT ON (value) value, label
FROM numbers
ORDER BY value, priority;
使用DISTINCT ON通常比涉及组或窗口函数的其他方法更快