我正在使用Postgres 9.3。
我有两个表T1 以及它们之间T2的n:m关系T1_T2_rel。现在,我想创建一个视图,该视图除了T1的列外,还为T1中的每个记录提供一列,其中包含一个包含T2所有相关记录的主键ID的数组。如果T2中没有相关条目,则此列的相应字段应包含空值。
T1
T2
n:m
T1_T2_rel
我的架构的抽象版本如下所示:
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原则上简单地使用子查询就足够了。但是,如果我比较查询计划,则在使用a时使用left joinPost索引扫描时,Postgres将对聚合表采取顺序扫描left join lateral。
left join
left join lateral
正如@Denis已经评论过的:不需要LATERAL。另外,您的子查询选择了错误的列。这有效:
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提琴。
关于随后的顺序扫描,您提到:如果查询整个表,顺序扫描通常会 更快 。取决于运行的版本,硬件,基数和数据分布的设置和统计信息。WHERE试用诸如WHERE t1.t1_id < 1000或的选择子句,WHERE t1.t1_id = 1000并与计划程序设置结合以了解选择:
WHERE
WHERE t1.t1_id < 1000
WHERE t1.t1_id = 1000
SET enable_seqscan = off; SET enable_indexscan = off;
重置:
RESET enable_seqscan; RESET enable_indexscan;