阅读(546) (12)

PostgreSQL intagg

2021-08-24 15:40:01 更新
F.17.1. 函数
F.17.2. 使用示例

intagg模块提供了一个整数聚集器以及一个枚举器。intagg现在已被弃用,因为有内建的函数能提供其功能的超集。不过,该模块仍然作为内建函数的一个兼容性包装器被提供。

F.17.1. 函数

聚集器是一个聚集函数int_array_aggregate(integer),它产生一个完全包含输入整数的整数数组。这是一个array_agg的包装器,它对任何数组类型做同样的事情。

枚举器是一个函数int_array_enum(integer[]),它返回setof integer。它本质上是聚集器的一个逆操作:给定一个整数数组,将它展开成行的集合。这是unnest的一个包装器,它对任何数组类型做相同的事情。

F.17.2. 使用示例

很多数据库系统都有一对多表的概念这样一个表通常位于两个被索引表之间,例如:

CREATE TABLE left (id INT PRIMARY KEY, ...);
CREATE TABLE right (id INT PRIMARY KEY, ...);
CREATE TABLE one_to_many(left INT REFERENCES left, right INT REFERENCES right);

通常这样用它:

SELECT right.* from right JOIN one_to_many ON (right.id = one_to_many.right)
  WHERE one_to_many.left = item;

这将为左手表中的一个项返回右手表中的所有项。这在 SQL 中是很常见的结构。

现在,这种方法对于具有非常多项的one_to_many表会有麻烦。通常,这样的一次连接将为一个特定的左手项导致一次索引扫描以及为每一个右手项导致一次取出。如果你有一个非常动态的系统,你没什么可做的。但是,如果你的数据相对比较静态,你可以使用聚集器创建一个汇总表。

CREATE TABLE summary AS
  SELECT left, int_array_aggregate(right) AS right
  FROM one_to_many
  GROUP BY left;

这将创建一个表,其中对每一个左项都有一行,并且有一个右项的数组。现在如果没有某种方法来使用该数组,这样做是没有任何用处的。这时数组枚举器就派上用场了,你可以

SELECT left, int_array_enum(right) FROM summary WHERE left = item;

上述使用int_array_enum的查询产生与以下语句相同的结果

SELECT left, right FROM one_to_many WHERE left = item;

区别是针对汇总表的查询必须只从表中得到一行,而针对one_to_many的直接查询必须索引扫描并且为每项都获取一行。

在一个系统上,一个EXPLAIN显示一个查询的代价从 8488 降低到了 329。原始查询是一个涉及到one_to_many表的连接,它被替换为:

SELECT right, count(right) FROM
  ( SELECT left, int_array_enum(right) AS right
    FROM summary JOIN (SELECT left FROM left_table WHERE left = item) AS lefts
         ON (summary.left = lefts.left)
  ) AS list
  GROUP BY right
  ORDER BY count DESC;