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;