sql - Left Join Lateral 和数组聚合

标签 sql postgresql aggregate-functions postgresql-9.3

我正在使用 Postgres 9.3。

我有两个表 T1T2 以及它们之间的 n:m 关系 T1_T2_rel。现在我想创建一个 View ,除了 T1 的列之外,还提供一个列,对于 T1 中的每条记录,该列包含一个数组,其中包含 T2 的所有相关记录的主键 ID。如果T2中没有相关条目,则该列对应字段为空值。

我的架构的抽象版本如下所示:

CREATE TABLE T1 ( t1_id serial primary key, t1_data int );

CREATE TABLE T2 ( t2_id serial primary key );

CREATE TABLE T1_T2_rel (
  t1_id int references T1( t1_id )
, t2_id int references T2( t2_id )
);

对应的样本数据可以生成如下:

INSERT INTO T1 (t1_data)
SELECT cast(random()*100 as int) FROM generate_series(0,9) c(i);

INSERT INTO T2 (t2_id) SELECT nextval('T2_t2_id_seq') FROM generate_series(0,99);

INSERT INTO T1_T2_rel
SELECT cast(random()*10 as int) % 10 + 1 as t1_id
     , cast(random()*99+1 as int) as t2_id
FROM   generate_series(0,99);

到目前为止,我提出了以下查询:

SELECT T1.t1_id, T1.t1_data, agg
FROM T1
LEFT JOIN LATERAL (
   SELECT t1_id, array_agg(t2_id) as agg
   FROM T1_T2_rel
   WHERE t1_id=T1.t1_id
   GROUP BY t1_id
   ) as temp ON temp.t1_id=T1.t1_id;

这行得通。但是,可以简化吗?

相应的 fiddle 可以在这里找到:sql-fiddle .不幸的是,sql-fiddle 还不支持横向连接所需的 Postgres 9.3。

[更新] 正如已经指出的那样,原则上使用子查询的简单 left join 就足够了。但是,如果我比较查询计划,Postgres 在使用 left join 时对聚合表求助于顺序扫描,而在 left join lateral 的情况下使用索引扫描.

最佳答案

正如@Denis 已经评论过的:不需要 LATERAL . 此外,您的子查询选择了错误的列。这有效:

SELECT t1.t1_id, t1.t1_data, t2_ids
FROM   t1
LEFT   JOIN (
    SELECT t1_id, array_agg(t2_id) AS t2_ids
    FROM   t1_t2_rel
    GROUP  BY 1
    ) sub USING (t1_id);

-SQL fiddle.

性能和测试

关于随后的顺序扫描,您提到:如果您查询整个表,顺序扫描通常更快。取决于您运行的版本、您的硬件、您的设置和基数统计以及您的数据分布。选择性实验 WHERE类似 WHERE t1.t1_id < 1000 的条款或 WHERE t1.t1_id = 1000并结合 planner settings了解选择:

SET enable_seqscan = off;
SET enable_indexscan = off;

重置:

RESET enable_seqscan;
RESET enable_indexscan;

请注意,仅在您的本地 session 中! This related answer on dba.SE有更多说明。
当然,您的设置也可能关闭:

关于sql - Left Join Lateral 和数组聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19490861/

相关文章:

sql - "entity"特定序列

sql - 根据观看的节目类别将一条记录拆分为多条记录

sql - 我可以基于多列聚合数据库数据吗?

sql - 如何使用 rownum 显示给定表的第一行和最后一行而不使用 union 子句

sql - Oracle 其中 'TEXT' 按一定百分比匹配某些内容

mysql - 我应该将多个 ID 存储为 varchar 并使用 FIND_IN_SET 吗?

mysql - UPDATE 语句格式类似于 INSERT 语句

PostgreSQL 错误 : could not receive data from client: An operation was attempted on something that is not a socket

postgresql - sqlalchemy,违反外键约束

sql - 为从 Postgres 中提取的列列表中的值创建数组