Выберите общие значения при использовании группы по [Postgres] - PullRequest
2 голосов
/ 04 августа 2020

У меня есть три основных стола, встречи, лица, хобби с двумя реляционными таблицами.

Table meetings
+---------------+
| id | subject  |
+----+----------+
|  1 | Kickoff  |
|  2 | Relaunch |
|  3 | Party    |
+----+----------+
Table persons
+------------+
| id | name  |
+----+-------+
|  1 | John  |
|  2 | Anna  |
|  3 | Linda |
+----+-------+
Table hobbies
+---------------+
| id | name     |
+----+----------+
|  1 | Soccer   |
|  2 | Tennis   |
|  3 | Swimming |
+----+----------+
Relation Table meeting_person
+-----------------+-----------+
| id | meeting_id | person_id |
+----+------------+-----------+
|  1 |          1 |         1 |
|  2 |          1 |         2 |
|  3 |          1 |         3 |
|  4 |          2 |         1 |
|  5 |          2 |         2 |
|  6 |          3 |         1 |
+----+------------+-----------+
Relation Table person_hobby
+----------------+----------+
| id | person_id | hobby_id |
+----+-----------+----------+
|  1 |         1 |        1 |
|  2 |         1 |        2 |
|  3 |         1 |        3 |
|  4 |         2 |        1 |
|  5 |         2 |        2 |
|  6 |         3 |        1 |
+----+-----------+----------+

Теперь я хочу найти общие увлечения всех человек, посещающий каждую встречу. Таким образом, желаемый результат будет:

+------------+-----------------+------------------------+
| meeting_id | persons         | common_hobbies         |
|            | (Aggregated)    | (Aggregated)           |
+------------+-----------------+------------------------+
|          1 | John,Anna,Linda | Soccer                 |
|          2 | John,Anna       | Soccer,Tennis          |
|          3 | John            | Soccer,Tennis,Swimming |
+------------+-----------------+------------------------+

Моя текущая работа в процессе:

select
    m.id as "meeting_id", 
    (
        select string_agg(distinct p.name, ',')
        from meeting_person mp
        inner join persons p on mp.person_id = p.id
        where m.id = mp.meeting_id
    ) as "persons",
    string_agg(distinct h2.name , ',') as "common_hobbies"
from meetings m
inner join meeting_person mp2 on m.id = mp2.meeting_id 
inner join persons p2 on mp2.person_id = p2.id
inner join person_hobby ph2 on p2.id = ph2.person_id 
inner join hobbies h2 on ph2.hobby_id = h2.id 
group by m.id

Но в этом запросе перечислены не common_hobbies, а все хобби, которые хотя бы раз упоминались.

+------------+-----------------+------------------------+
| meeting_id | persons         | common_hobbies         |
+------------+-----------------+------------------------+
|          1 | John,Anna,Linda | Soccer,Tennis,Swimming |
|          2 | John,Anna       | Soccer,Tennis,Swimming |
|          3 | John            | Soccer,Tennis,Swimming |
+------------+-----------------+------------------------+

Кто-нибудь может мне подсказать, как я могу решить эту проблему?

Ура

1 Ответ

1 голос
/ 05 августа 2020

Эту проблему можно решить, реализовав настраиваемую функцию агрегирования ( нашел здесь ):

create or replace function array_intersect(anyarray, anyarray)
returns anyarray language sql
as $$
    select 
        case 
            when $1 is null then $2
            when $2 is null then $1
            else
                array(
                    select unnest($1)
                    intersect
                    select unnest($2))
        end;
$$;

create aggregate array_intersect_agg (anyarray)
(
    sfunc = array_intersect,
    stype = anyarray
);

Итак, решение может быть следующим:

select 
    meeting_id, 
    array_agg(ph.name) persons, 
    array_intersect_agg(hobby) common_hobbies
from meeting_person mp
join (
    select p.id, p.name, array_agg(h.name) hobby
    from person_hobby ph
    join persons p on ph.person_id = p.id
    join hobbies h on h.id = ph.hobby_id
    group by p.id, p.name
) ph on ph.id = mp.person_id
group by meeting_id;

Посмотрите на пример скрипки

Результат:

meeting_id |    persons            | common_hobbies
-----------+-----------------------+--------------------------
1          |    {John,Anna,Linda}  | {Soccer}
3          |    {John}             | {Soccer,Tennis,Swimming}
2          |    {John,Anna}        | {Soccer,Tennis}
...