Postgresql 9.3 - array_agg challenge

Dow*_*wie 5 postgresql many-to-many aggregate-functions

我正在尝试理解Postgresql 9.3中的array_agg函数.我为每个可能有兴趣参与的人提供了一个有趣的例子.

20世纪80年代的任何一部美国电影迷都可能熟悉一起出现在很多热门影片中的"小伙子".在wikipedia上使用关于brat pack影片的信息,我创建了表格,当它们连接在一起时,可以告诉我们谁在彼此工作 - 如果我们有正确的查询!

/*
See:  http://en.wikipedia.org/wiki/Brat_Pack_(actors)
*/

CREATE TABLE actor(
    id SERIAL PRIMARY KEY, 
    name VARCHAR(50)
);
insert into actor(name) values ('Emilio Estevez'),('Anthony Michael Hall'),('Rob Lowe'),('Andrew McCarthy'),('Demi Moore'),('Judd Nelson'),('Molly Ringwald'),('Ally Sheedy')

CREATE TABLE movie(
    id SERIAL PRIMARY KEY, 
    title VARCHAR(200)
);
insert into movie(title) values ('The Outsiders'),('Class'),('Sixteen Candles'),('Oxford Blues'),('The Breakfast Club'),('St. Elmos Fire'),
('Pretty in Pink'),('Blue City'),('About Last Night'),('Wisdom'), ('Fresh Horses'),('Betsys Wedding'),('Hail Caesar');

CREATE TABLE movie_brats(
    id SERIAL PRIMARY KEY, 
    movie_id INT REFERENCES movie(id), 
    actor_id INT REFERENCES actor(id)
);
insert into movie_brats(movie_id, actor_id) values (1,1),(1,3),(2,3),(2,4),(3,2),(3,7),(4,3),(4,8),(5,1),(5,2),(5,6),
(5,7),(5,8),(6,1),(6,3),(6,4),(6,5),(6,6),(6,8),(7,4),(7,7),(8,6),(8,8),(9,3),(9,5),(10,1),(10,5),(11,4),(11,7),
(12,7),(12,8),(13,2),(13,6);
Run Code Online (Sandbox Code Playgroud)

查询:显示brat包的每个成员使用的不同列表,在两列中按名称排序

 Name                      Worked With
 ----------------------------------------------------------------------------------------------------------------
Emelio Estevez       |  Emilio Estevez, Anthony Michael Hall, Rob Lowe, Andrew McCarthy, Demi Moore, Judd Nelson, Molly Ringwald, Ally Sheedy
*/
Run Code Online (Sandbox Code Playgroud)

我破碎的查询:

select a1.name, array_to_string(array_agg(a2.name),', ') as Co_Stars
from actor a1, actor a2, movie m, movie_brats mb
where 
    m.id = mb.movie_id
    and a1.id = mb.actor_id
    and a2.id = mb.actor_id
group by a1.id
Run Code Online (Sandbox Code Playgroud)

Clo*_*eto 1

SQL小提琴

with v as (
    select
        a.id as actor_id,
        a.name as actor_name,
        m.id as m_id
    from
        actor a
        inner join
        movie_brats mb on a.id = mb.actor_id
        inner join
        movie m on m.id = mb.movie_id
)
select
    v1.actor_name as "Name",
    string_agg(
        distinct v2.actor_name, ', ' order by v2.actor_name
    ) as "Worked With"
from
    v v1
    left join
    v v2 on v1.m_id = v2.m_id and v1.actor_id != v2.actor_id
group by 1
order by 1
Run Code Online (Sandbox Code Playgroud)

上面的不同聚合是必要的,以免显示重复的名字,以防他们在多部电影中合作。

left join必要不要压制未与列表中任何其他人合作的演员,就像inner join.

如果你想展示他们在哪部电影中合作:SQL Fiddle

with v as (
    select
        a.id as actor_id,
        a.name as actor_name,
        m.id as m_id,
        m.title as title
    from
        actor a
        inner join
        movie_brats mb on a.id = mb.actor_id
        inner join
        movie m on m.id = mb.movie_id
)
select
    a1 as "Name",
    string_agg(
        format('%s (in %s)', a2, title), ', '
        order by format('%s (in %s)', a2, title)
    ) as "Worked With"
from (
    select 
        v1.actor_name as a1,
        v2.actor_name as a2,
        string_agg(v1.title, ', ' order by v1.title) as title    
    from
        v v1
        left join
        v v2 on v1.m_id = v2.m_id and v1.actor_id != v2.actor_id
    group by 1, 2
) s
group by 1
order by 1
Run Code Online (Sandbox Code Playgroud)