如何用 LEFT JOIN 替换 SELECT 子句中的多个相关子查询?

Jar*_*bzo 5 postgresql

我的问题的小提琴可以在https://dbfiddle.uk/?rdbms=postgres_10&fiddle=e387589d446d9c9a952294f8c7a98494上找到。

我有简单的表格布局:

class
person: belongs to a class
room:   belongs to a class
Run Code Online (Sandbox Code Playgroud)

以下查询选择嵌入了人员的所有类:

select    class.identifier, array(select person.identifier from person where person.class_identifier = class.identifier) as persons
from      class
order by  class.identifier;
Run Code Online (Sandbox Code Playgroud)

在试验和学习更多有关相关子查询的信息时,我注意到可以通过用 aLEFT JOIN组合替换相关子查询来重写上面的查询GROUP BY

select     class.identifier, array_agg(person.identifier) as persons
from       class
left join  (
             select  person.identifier, person.class_identifier
             from    person
           ) as person
on         class.identifier = person.class_identifier
group by   class.identifier
order by   class.identifier;
Run Code Online (Sandbox Code Playgroud)

请注意,我假设每个班级至少有一个人。如果没有,我可以添加coalesce()周围json_agg

在我的第二种情况下,我将选择所有嵌入人员房间的类。让我们首先以与上面第一个查询相同的方式编写:

select    class.identifier, array(select person.identifier from person where person.class_identifier = class.identifier) as persons,
                            array(select room.identifier from room where room.class_identifier = class.identifier) as rooms
from      class
order by  class.identifier;
Run Code Online (Sandbox Code Playgroud)

这给出了预期的结果。

现在我想重复我之前所做的:引入LEFT JOINs。我的第一次尝试如下:

select     class.identifier, array_agg(person.identifier) as persons
                           , array_agg(room.identifier) as rooms
from       class
left join  (
             select  person.identifier, person.class_identifier
             from    person
           ) as person
on         class.identifier = person.class_identifier
left join  (
             select  room.identifier, room.class_identifier
             from    room
           ) as room
on         class.identifier = room.class_identifier
group by   class.identifier
order by   class.identifier;
Run Code Online (Sandbox Code Playgroud)

现在我得到了错误的结果。人或房间在输出数组中重复。我理解为什么会发生这种情况(我们正在对classpersonroom之间的笛卡尔积进行分组,因此每个都对每个房间重复,反之亦然),但我不知道如何进行。

我怎样才能在这里继续?是否可以开始替换SELECT子句中的多个相关子查询LEFT JOIN + GROUP BY,还是需要其他技巧?

Len*_*art 4

您可以将不同的值聚合为:

array_agg(distinct person.identifier)
Run Code Online (Sandbox Code Playgroud)

但是,我不确定您为什么要加入这些子选择。您可以直接加入表,例如:

select class.identifier, array_agg(distinct person.identifier) as persons
                       , array_agg(distinct room.identifier) as rooms
from class
left join  person
    on class.identifier = person.class_identifier
left join room
    on class.identifier = room.class_identifier
group by   class.identifier
order by   class.identifier;
Run Code Online (Sandbox Code Playgroud)

我还建议您为表使用别名。1 个或 2 个字母的缩写将使查询更易于阅读 (IMO):

select c.identifier, array_agg(distinct p.identifier) as persons
                   , array_agg(distinct r.identifier) as rooms
from class c
left join  person p
    on c.identifier = p.class_identifier
left join room r
    on c.identifier = r.class_identifier
group by   c.identifier
order by   c.identifier;
Run Code Online (Sandbox Code Playgroud)

哦,欢迎来到该网站。第一个问题很好,包含代码和示例数据,做得很好。