将查询中的单个列聚合为多个列

Aggregate a single column in query with many columns

提问人:Fabrício Matté 提问时间:4/15/2013 最后编辑:CommunityFabrício Matté 更新时间:7/12/2016 访问量:7155

问:

当我在查询中有许多其他列时,是否有适当的方法来聚合单个列?

我已经尝试了这个有效的答案,但我的查询变得更加冗长。

我当前的查询如下所示:

SELECT t1.foo1, t1.foo2, t2.foo3, t2.foo4, string_agg(t3.aggregated_field, ', ')
FROM tbl1 t1
LEFT JOIN tbl2 t2 ON t1.id = t2.fkeyid
LEFT JOIN tbl3 t3 ON t2.id = t3.fkeyid
GROUP BY t1.foo1, t1.foo2, t2.foo3, t2.foo4, t2.foo5, t2.foo6
ORDER BY t2.foo5, t2.foo6

查询有更多的字段和 s,重要的部分是所有这些字段都具有 1 到 1 或 1 到 0 的关系,除了我想聚合的 1 到 n 的字段,在上面的伪查询中表示。LEFT JOINt3.aggregated_field

由于我使用的是聚合函数,因此 和 中列出的所有字段都必须是聚合的或子句的一部分。这使我的查询方式比现在更冗长。SELECTORDER BYGROUP BY

也就是说,假设是主键,当这个字段重复时,除此字段外的所有其他字段也相等。我希望这些重复的行作为具有聚合字段值的单行结果。(基本上是带有聚合列的)foo1aggregated_fieldselect distinct

有没有更好的方法可以做到这一点(而不必将所有其他字段放在 中),或者我应该在后端遍历结果集,为获取此 1 到 n 关系的每一行执行查询?GROUP BY


服务器运行的是 PostgreSQL 9.1.9,更具体地说:

x86_64-unknown-linux-gnu 上的 PostgreSQL 9.1.9,由 gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-54) 编译,64 位

SQL PostgreSQL 聚合函数

评论

1赞 Erwin Brandstetter 4/16/2013
为什么开发人员透露他所询问的软件版本。为什么?在 SO 上,这是一个痛苦的大众现象。这就像本来非常聪明的人一旦提出问题就会立即变成假人。提供您的软件版本和您的问题。这应该不言而喻。
0赞 Fabrício Matté 4/16/2013
@ErwinBrandstetter 我的错,版本是 9.0+,我将连接到服务器的网络,这样我就可以在添加问题之前检查确切的版本。
0赞 Erwin Brandstetter 4/16/2013
谢谢。我的评论是一直在积累的挫败感的结果。它应该如此明显。然而,很多人没有想到它。甚至是高声誉的人。顺便说一句,9.1 - 你在那里很幸运。我的答案应该对你有用。
0赞 Fabrício Matté 4/16/2013
@ErwinBrandstetter 是的,我明白。虽然隐式使用使它成为pgsql9+,但我应该知道比次要版本有重大变化,我承认我没有事先检查版本是懒惰的。浏览答案非常有意义,当我有时间应用和测试它时,我会在一个小时左右提供反馈。string_agg=]
0赞 Erwin Brandstetter 4/16/2013
顺便说一句,主要版本在 PostgreSQL 中包括点后的第一个数字。更多关于该项目的官方版本控制网站。

答:

1赞 Clodoaldo Neto 4/15/2013 #1

如果主要问题是计算字段 (foox),那么这会有所帮助:

SELECT foo1, foo2, foo3, foo4, foo5, foo6, string_agg(aggregated_field, ', ')
FROM tbl1
GROUP BY 1, 2, 3, 4, 5, 6
ORDER BY 5, 6

这些字段是按它们在选择列表中显示的顺序排列的字段。1, 2...

评论

0赞 Fabrício Matté 4/15/2013
虽然不理想,但我想这会减少我要求的冗长。今天中午会做更多的研究。
0赞 Fabrício Matté 4/16/2013
效果很好,只是一个注释:除非它们也在列表中,否则不能以这种方式枚举。foo7foo8SELECT
0赞 Clodoaldo Neto 4/16/2013
@FabrícioMatté 是的,我只是复制并猜测它们在真正的选择列表中。
0赞 Fabrício Matté 4/16/2013
我会保留 +1,因为它对我有用,但是当您在选择中有 30 个字段而在 中有更多字段时,这不是很可维护。ORDER BY=]
6赞 Erwin Brandstetter 4/16/2013 #2

简单查询

使用 PostgreSQL 9.1 或更高版本,这要简单得多。正如这个密切相关的答案所解释的:

对于一个表的主键来说就足够了。因为:GROUP BY

foo1 是主键

..您可以将示例简化为:

SELECT foo1, foo2, foo3, foo4, foo5, foo6, string_agg(aggregated_field, ', ')
FROM   tbl1
GROUP  BY 1
ORDER  BY foo7, foo8;  -- have to be spelled out, since not in select list!

使用多个表进行查询

但是,由于您有:

还有更多字段和 LEFT JOIN,重要的部分是所有这些字段都具有 1 比 1 或 1 比 0 的关系,除了我想聚合的 1 到 n 的字段

..先聚合,后加入应该更快、更简单:

SELECT t1.foo1, t1.foo2, ...
     , t2.bar1, t2.bar2, ...
     , a.aggregated_col 
FROM   tbl1 t1
LEFT   JOIN tbl2 t2 ON ...
...
LEFT   JOIN (
   SELECT some_id, string_agg(agg_col, ', ') AS aggregated_col
   FROM   agg_tbl a ON ...
   GROUP  BY some_id
   ) a ON a.some_id = ?.some_id
ORDER  BY ...

这样,查询的大部分根本不需要聚合。

我最近在 SQL Fiddle 中提供了一个测试用例来证明这个相关答案中的观点:

既然你指的是这个相关的答案:不,在这种情况下根本无济于事。DISTINCT

评论

0赞 Fabrício Matté 4/16/2013
是的,几个小时前我注意到在这种情况下无济于事。当我回到家时,我会检查你的答案。DISTINCT=]
0赞 Fabrício Matté 4/16/2013
根据我的理解,子查询将隐式创建一个临时表,其中包含连接前整个表的聚合。在这种情况下,如果我在子查询中放入一个子句,我可以对其进行一些优化,对吗?这看起来是最好的方法,我会根据我的需求进行调整。谢谢。WHERE
0赞 Erwin Brandstetter 4/16/2013
@FabrícioMatté:WHERE 子句可能很有用,尤其是当您有一个与之对应的索引时。但是,根据整个查询,Postgres 查询计划器可能会使用不同的计划,以它期望最快的计划为准(这就是正确配置的计划器成本常量的用武之地)。它不一定是“临时表”(具体化步骤)。测试以获取详细信息。EXPLAIN ANALYZE