使用TOP BY GROUP BY之类的东西

use*_*440 9 sql postgresql aggregate greatest-n-per-group

table1如下表所示

+--------+-------+-------+------------+-------+
| flight |  orig |  dest |  passenger |  bags |
+--------+-------+-------+------------+-------+
|   1111 |  sfo  |  chi  |  david     |     3 |
|   1112 |  sfo  |  dal  |  david     |     7 |
|   1112 |  sfo  |  dal  |  kim       |     10|
|   1113 |  lax  |  san  |  ameera    |     5 |
|   1114 |  lax  |  lfr  |  tim       |     6 |
|   1114 |  lax  |  lfr  |  jake      |     8 |
+--------+-------+-------+------------+-------+
Run Code Online (Sandbox Code Playgroud)

我在orig下面汇总表格

select 
  orig
  , count(*) flight_cnt
  , count(distinct passenger) as pass_cnt
  , percentile_cont(0.5) within group ( order by bags ASC) as bag_cnt_med
from table1
group by orig
Run Code Online (Sandbox Code Playgroud)

我需要为每个组添加passenger最长的名字(length(passenger))orig- 我该怎么做呢?

预期产出

+------+-------------+-----------+---------------+-------------------+
| orig |  flight_cnt |  pass_cnt |  bags_cnt_med | pass_max_len_name |
+------+-------------+-----------+---------------+-------------------+
| sfo  |           3 |         2 |             7 |  david            |
| lax  |           3 |         3 |             6 | ameera            |
+------+-------------+-----------+---------------+-------------------+
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 5

您可以方便地检索每组最长名称的乘客DISTINCT ON.

但我认为没有办法将这个(或任何其他简单的方法)与原始查询结合在一起SELECT.我建议加入两个独立的子查询:

SELECT *
FROM  (  -- your original query
   SELECT orig
        , count(*) AS flight_cnt
        , count(distinct passenger) AS pass_cnt
        , percentile_cont(0.5) WITHIN GROUP (ORDER BY bags) AS bag_cnt_med
   FROM   table1
   GROUP  BY orig
   ) org_query
JOIN  (  -- my addition
   SELECT DISTINCT ON (orig) orig, passenger AS pass_max_len_name
   FROM   table1
   ORDER  BY orig, length(passenger) DESC NULLS LAST
   ) pas USING (orig);
Run Code Online (Sandbox Code Playgroud)

USING在join子句中方便地只输出一个实例orig,所以你可以简单地SELECT *在外部使用SELECT.

如果passenger可以为NULL,则添加NULLS LAST以下内容非常重要:

从同一组中具有相同最大长度的多个乘客名称,您可以获得任意选择 - 除非您添加更多表达式ORDER BY作为决胜局.以上链接中的详细解释.

性能?

通常,单次扫描是优越的,特别是对于顺序扫描.

上述查询使用两次扫描(可能是索引/索引扫描).但是第二次扫描比较便宜,除非桌子太大而不适合缓存(大多数情况下).卢卡斯建议的替换查询仅具有单个 SELECT加入:

, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1]  -- I'd add NULLS LAST
Run Code Online (Sandbox Code Playgroud)

这个想法是聪明,但我最后一次测试,array_aggORDER BY没有如此上佳表现.(每组的开销ORDER BY很大,阵列处理也很昂贵.)

对于像Postgres Wiki中指示的自定义聚合函数,相同的方法可以更便宜.或者,更快,但是,使用C语言编写的版本,可在PGXN上使用.消除了阵列处理的额外成本,但我们仍需要每组.只有少数几个团体可能会更快.然后你会添加:first() ORDER BY

 , first(passenger ORDER BY length(passenger) DESC NULLS LAST)
Run Code Online (Sandbox Code Playgroud)

GordonLukas也提到了窗口功能first_value().窗口函数聚合函数之后应用.为了在同一个中使用它SELECT,我们需要首先以passenger 某种方式聚合- 捕获22.Gordon用子查询解决了这个问题 - 另一个候选人用标准的Postgres获得了良好的性能.

first()没有子查询也一样,应该更简单,更快一点.但DISTINCT ON对于大多数情况而言,每组只有少量行,它仍然不会比单独的快.对于每组的大量行,递归CTE技术通常更快.如果您有一个包含所有相关唯一orig值的单独表,那么还有更快的技术.细节:

最佳解决方案取决于各种因素.布丁的证据就在于吃.要优化性能,您必须使用您的设置进行测试.以上查询应该是最快的.