如何将 ORDER BY 和 LIMIT 与聚合函数结合使用?

Jar*_*bzo 9 postgresql aggregate order-by subquery

我的问题的小提琴可以在https://dbfiddle.uk/?rdbms=postgres_10&fiddle=3cd9335fa07565960c1837aa65143685上找到。

我有一个简单的表格布局:

class
person: belongs to a class
Run Code Online (Sandbox Code Playgroud)

我想选择所有班级,对于每个班级,我想要按降序排列的所属人员的前两个人员标识符。

我通过以下查询解决了这个问题:

select     c.identifier, array_agg(p.identifier order by p.name desc) as persons
from       class as c
left join lateral (
             select   p.identifier, p.name
             from     person as p
             where    p.class_identifier = c.identifier
             order by p.name desc
             limit    2
           ) as p
on         true
group by   c.identifier
order by   c.identifier
Run Code Online (Sandbox Code Playgroud)

注意:我可以在SELECT子句中使用相关子查询,但作为学习过程的一部分,我试图避免这种情况。

如您所见,我order by p.name desc在两个地方申请:

  • 在子查询中
  • 在聚合函数中

有没有办法避免这种情况?我的坚持:

  • 首先,显然我不能删除order by子查询中的 ,因为这会给出一个不符合我上述要求的查询。

  • 其次,我认为order by聚合函数中的 不能被遗漏,因为子查询的行顺序不一定保留在聚合函数中?

我应该重写查询吗?

Erw*_*ter 6

order by p.name desc在两个地方申请......有没有办法避免这种情况?

是的。直接在横向子查询中使用ARRAY 构造函数聚合:

SELECT c.identifier, p.persons
FROM   class c
CROSS  JOIN LATERAL (
   SELECT ARRAY (
      SELECT identifier
      FROM   person
      WHERE  class_identifier = c.identifier
      ORDER  BY name DESC
      LIMIT  2
      ) AS persons
   ) p
ORDER  BY c.identifier;
Run Code Online (Sandbox Code Playgroud)

你也不需要GROUP BY在外面SELECT这样。更短、更干净、更快。

因为 ARRAY 构造函数总是准确返回 1 行,所以我LEFT JOIN用一个普通的替换了CROSS JOIN。(就像你在评论中指出的那样。)

db<>在这里小提琴

有关的:

子查询中的行顺序

要解决您的评论

我了解到子查询中的行顺序永远不能保证在外部查询中保留。

嗯,不。虽然 SQL 标准不提供任何保证,但Postgres 中的保证有限手册:

默认情况下,此顺序未指定,但可以通过ORDER BY在聚合调用中编写子句来控制,如第 4.2.7 节所示 。或者,从排序的子查询提供输入值通常会起作用。例如:

SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
Run Code Online (Sandbox Code Playgroud)

请注意,如果外部查询级别包含附加处理(例如连接),则此方法可能会失败,因为这可能会导致在计算聚合之前对子查询的输出进行重新排序。

如果您在下一级别中所做的只是聚合行,则肯定会保证顺序。是的,我们提供给 ARRAY 构造函数的也是一个子查询。这不是重点。它也适用于array_agg()

SELECT c.identifier, p.persons
FROM   class c
CROSS  JOIN LATERAL (
   SELECT array_agg(identifier) AS persons
   FROM  (
      SELECT identifier
      FROM   person
      WHERE  class_identifier = c.identifier
      ORDER  BY name DESC
      LIMIT  2
      ) sub
   ) p
ORDER  BY c.identifier;
Run Code Online (Sandbox Code Playgroud)

但我希望 ARRAY 构造函数在这种情况下更快。看:

  • 说在两个查询中我们都可以用“横向连接”替换“左侧连接”是否正确?在没有人员的情况下,第一个查询返回一个空数组,第二个查询返回 null,对吗?这与 https://dba.stackexchange.com/questions/173831/convert-right-side-of-join-of-many-to-many-into-array/173879#173879 的最后一句话相矛盾,但我认为信息有误?我认为我们需要检查 `p.persons is not null`(在​​第一个查询的情况下)或 `p.persons != '{}'`(在第二个查询的情况下)以仅输出至少具有一个人? (2认同)

归档时间:

查看次数:

5936 次

最近记录:

6 年,7 月 前