intagg 模块提供一个整数聚合器与一个枚举器。intagg 现已过时,因为内置函数已提供其功能的超集。不过,该模块仍作为内置函数的兼容包装而被保留。
聚合器是聚合函数 int_array_aggregate(integer),它产生一个恰好包含其接收到的所有整数的整型数组。它是 array_agg 的包装,后者可对任意数组类型完成同样的工作。
枚举器是函数 int_array_enum(integer[]),返回 setof integer。它本质上是聚合器的逆操作:给定一个整数数组,将其展开为一组行。它是 unnest 的包装,后者可对任意数组类型完成同样的工作。
许多数据库系统都有多对多表的概念。这样的表通常位于两个已建立索引的表之间,例如:
CREATE TABLE left_table (id INT PRIMARY KEY, ...);
CREATE TABLE right_table (id INT PRIMARY KEY, ...);
CREATE TABLE many_to_many(id_left INT REFERENCES left_table,
id_right INT REFERENCES right_table);
它的典型用法如下:
SELECT right_table.*
FROM right_table JOIN many_to_many ON (right_table.id = many_to_many.id_right)
WHERE many_to_many.id_left = item;
这将针对左表中的一个条目返回右表中的所有相关条目。这是 SQL 中非常常见的结构。
当 many_to_many 表中的条目数量非常大时,这种方法可能变得繁琐。对于这样的连接,左表的一个条目通常会导致对表中每个右侧条目各执行一次索引扫描与取行。如果你的系统变化非常频繁,那就没有太多办法。但如果你的某些数据相当静态,你可以使用聚合器创建一个汇总表。
CREATE TABLE summary AS SELECT id_left, int_array_aggregate(id_right) AS rights FROM many_to_many GROUP BY id_left;
这将创建一个表,其中每个左侧条目占一行,并带有一个右侧条目的数组。如果没有使用该数组的方法,这个表就没什么用处;这正是存在数组枚举器的原因。你可以执行
SELECT id_left, int_array_enum(rights) FROM summary WHERE id_left = item;
上面使用 int_array_enum 的查询产生的结果与
SELECT id_left, id_right FROM many_to_many WHERE id_left = item;
相同。区别在于针对汇总表的查询只需从表中获取一行,而直接查询 many_to_many 则必须为每个条目执行索引扫描并取回一行。
在某个系统上,EXPLAIN 显示一条代价为 8488 的查询被降低到 329。原查询是一个涉及 many_to_many 表的连接,它被替换为:
SELECT id_right, count(id_right) FROM
( SELECT id_left, int_array_enum(rights) AS id_right
FROM summary
JOIN (SELECT id FROM left_table
WHERE id = item) AS lefts
ON (summary.id_left = lefts.id)
) AS list
GROUP BY id_right
ORDER BY count DESC;