Eva*_*oll 16 postgresql benchmark aggregate array
我刚刚查看了一些为8.4 之前的 PostgreSQL编写的旧代码,我看到了一些非常棒的东西。我记得以前有一个自定义函数可以做一些这样的事情,但我忘记了预先的array_agg()
样子。回顾一下,现代聚合是这样写的。
SELECT array_agg(x ORDER BY x DESC) FROM foobar;
Run Code Online (Sandbox Code Playgroud)
然而,曾几何时,它是这样写的,
SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);
Run Code Online (Sandbox Code Playgroud)
所以,我用一些测试数据试了一下..
CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
AS t(x);
Run Code Online (Sandbox Code Playgroud)
结果令人惊讶..#OldSchoolCool 方式要快得多:加速了 25%。此外,在没有ORDER 的情况下简化它,表现出同样的缓慢。
# EXPLAIN ANALYZE SELECT ARRAY(SELECT x FROM foobar);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Result (cost=104425.28..104425.29 rows=1 width=0) (actual time=1665.948..1665.949 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Seq Scan on foobar (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.032..716.793 rows=10000000 loops=1)
Planning time: 0.068 ms
Execution time: 1671.482 ms
(5 rows)
test=# EXPLAIN ANALYZE SELECT array_agg(x) FROM foobar;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=119469.60..119469.61 rows=1 width=32) (actual time=2155.154..2155.154 rows=1 loops=1)
-> Seq Scan on foobar (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.031..717.831 rows=10000000 loops=1)
Planning time: 0.054 ms
Execution time: 2174.753 ms
(4 rows)
Run Code Online (Sandbox Code Playgroud)
那么,这里发生了什么。为什么array_agg是一个内部函数,比规划器的 SQL 巫术慢得多?
在 x86_64-pc-linux-gnu 上使用“ PostgreSQL 9.5.5,由 gcc 编译(Ubuntu 6.2.0-5ubuntu12)6.2.0 20161005,64 位”
Erw*_*ter 22
ARRAY 构造函数没有任何“老派”或“过时” (就是这样ARRAY(SELECT x FROM foobar)
)。它一如既往地现代。将其用于简单的数组聚合。
也可以从子查询的结果构造一个数组。在这种形式中,数组构造函数是用关键字
ARRAY
后跟括号(未括号)的子查询编写的。
的聚合函数array_agg()
是更通用的,因为它可以被集成在一个SELECT
与多个列,在相同的可能更多的聚合列表SELECT
,并且可以与形成任意组GROUP BY
。而 ARRAY 构造函数只能从SELECT
返回单个列中返回单个数组。
我没有研究源代码,但显然更通用的工具也更昂贵。
一个显着区别:{}
如果没有行符合条件,则 ARRAY 构造函数返回一个空数组 ( )。array_agg()
返回NULL
相同。
pbi*_*len 11
我相信 Erwin 接受的答案可以添加以下内容。
通常,我们使用带有索引的常规表,而不是原始问题中的临时表(没有索引)。需要注意的是,ARRAY_AGG
在聚合期间完成排序时,聚合(例如 )无法利用现有索引。
例如,假设以下查询:
SELECT ARRAY(SELECT c FROM t ORDER BY id)
Run Code Online (Sandbox Code Playgroud)
如果我们在 上有一个索引t(id, ...)
,则可以使用该索引,以支持对 进行顺序扫描,t
然后对 进行排序t.id
。此外,如果包含在数组中的输出列(此处c
)是索引的一部分(例如上的索引t(id, c)
或包含的索引t(id) include(c)
),则这甚至可能是仅索引扫描。
现在,让我们重写该查询如下:
SELECT ARRAY_AGG(c ORDER BY id) FROM t
Run Code Online (Sandbox Code Playgroud)
现在,聚合将不使用索引,它必须对内存中的行进行排序(对于磁盘上的大型数据集甚至更糟)。这将始终是顺序扫描,t
然后是聚合+排序。
据我所知,这在官方文档中没有记录,但可以从源中获得。这应该适用于所有当前版本,包括 v11。