左连接横向和数组聚合

时间:2013-10-21 09:35:04

标签: sql postgresql aggregate-functions postgresql-9.3

我正在使用Postgres 9.3。

我有两个表T1T2以及n:m关系T1_T2_rel。现在我想创建一个视图,除了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;

这很有效。但是,它可以简化吗?

可在此处找到相应的小提琴:sql-fiddle。不幸的是,sql-fiddle不支持Postgres 9.3(尚未),这是横向连接所必需的。

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

1 个答案:

答案 0 :(得分:8)

正如@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.

性能和测试

关于随后的顺序扫描,您提到:如果查询整个表,顺序扫描通常更快。取决于您运行的版本,硬件,设置以及基数和数据分布的统计信息。尝试使用WHEREWHERE 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;

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