2014-06-26 80 views
5

我想了解Postgresql 9.3中的array_agg函數。我爲每個可能有興趣參與的人提供了一個有趣的例子。Postgresql 9.3 - array_agg挑戰

1980年代美國電影的任何粉絲都可能熟悉出現在許多熱門電影中的「小丑包」。通過使用維基百科關於小狗包裝電影的信息,我創建了表格,當它們連接在一起時,可以告訴我們誰與誰共事 - 如果我們有正確的查詢!

/* 
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); 

查詢:SHOW的不同列表誰的小子組的每個成員一起工作,通過名字在兩列有序

Name      Worked With 
---------------------------------------------------------------------------------------------------------------- 
Emelio Estevez  | Emilio Estevez, Anthony Michael Hall, Rob Lowe, Andrew McCarthy, Demi Moore, Judd Nelson, Molly Ringwald, Ally Sheedy 
*/ 

我破碎的查詢:

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 
+1

旁註:你可能想'string_agg()',而不是array_to_string的'組合()'和'ARRAY_AGG() ' –

+0

我更加簡潔的輸出 –

回答

1

SQL Fiddle

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 

以上不同的聚合是必要的不顯示的情況下,他們在一個以上的影片一起工作重複的名字。

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 
+1

@Dowwie我通過澄清更新了這個問題。 –

1

你的查詢的主要問題是,你(交叉)只加入movie_brats一次,所以每個演員都會印製每部電影(他/她玩的地方) - 這是更多的obvio我們,如果你改變你的查詢,使用內部連接(而不是交叉連接+ where)。

提示:

  • 沒有必要加入movie表,除非你想通過演員打印所有電影片名
  • 使用distinct,以避免重名
  • 過濾器通過a1.id <> a2.id避免演員被列爲他/她自己的工作。

Here is工作示例:

select a1.name, string_agg(distinct a2.name, ', ') as co_names 
from actor a1 
inner join movie_brats mb1 on a1.id = mb1.actor_id 
inner join movie_brats mb2 on mb1.movie_id = mb2.movie_id 
inner join actor a2 on a2.id = mb2.actor_id 
where a1.id <> a2.id 
group by a1.id 
+1

@Dowwie更新了第二個查詢,如果你不加入自己,你只會有行,它有一個'movie_id'和'actor_id'對。如果你加入它自己(使用'movie_id'字段),你將得到所有'actor_id'對的結果,其中'movie_id'是相同的 - 只有純粹的連接邏輯。 – pozs