Left Join 横向和数组聚合

Posted

技术标签:

【中文标题】Left Join 横向和数组聚合【英文标题】:Left Join Lateral and array aggregates 【发布时间】:2013-10-21 09:35:04 【问题描述】:

我使用的是 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 的情况下使用索引扫描。

【问题讨论】:

您能否提供一个包含简单数据样本的SQL Fiddle,供愿意尝试解决此问题的任何人使用? t1_id 的普通左加入组不也可以工作吗? 确实如此。谢谢,@丹尼斯 在这种情况下就足够了。但是,我抽象的太多了。 ;-) 生产代码有第三个表 T3 和 T1 和 T3 之间的另一个 n:m 关系,我想以相同的方式聚合。使用另一个左连接,第三个表中的每个键在第二个聚合中出现的次数与 T1_T2 关系中的条目一样多,反之亦然。 【参考方案1】:

正如@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 < 1000WHERE 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 有更多说明。 当然,您的设置也可能已关闭:

Keep PostgreSQL from sometimes choosing a bad query plan

【讨论】:

以上是关于Left Join 横向和数组聚合的主要内容,如果未能解决你的问题,请参考以下文章

hive 之 join 大法

如何使用 iPhone 模拟器测试横向视图?

深入理解css系列 -- 常规流下的块级盒的横向格式化模型

横向排列html代码

python数据分析(12)Pandas实现对Excel列表数据整合(merge纵向合并concat横向连接)

M-LAG数据中心常见组网架构(横向虚拟化技术)