2015-12-03 2 views
1

Я использую PostgreSQL 9.3.9 и есть таблица users, которая выглядит следующим образом:Как подсчитать количество соединений между пользователем и несколькими таблицами?

user_id | email 
---------------------------- 
1001  | [email protected] 
1030  | [email protected] 
2333  | [email protected] 
2502  | [email protected] 
3000  | [email protected] 
4000  | [email protected] 
4900  | [email protected] 

Я тогда несколько таблиц, которые перечисляют, что пользователи подключены на различных платформах и когда они подключены. Т.е. platform_a, platform_b, platform_c и т.д.

platform_a может выглядеть следующим образом:

user_id | created_at 
---------------------------- 
1001 | 2015-04-30 
2333 | 2015-05-15 
3000 | 2014-02-15 

platform_b может выглядеть следующим образом:

user_id | created_at 
---------------------------- 
1001 | 2015-06-30 
2333 | 2015-07-02 
4900 | 2015-07-03 

platform_c может выглядеть следующим образом:

user_id | created_at 
---------------------------- 
1001 | 2015-08-16 
1030 | 2015-07-03 
3000 | 2015-09-01 
4000 | 2015-09-01 

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

user_id | # of connections | latest created_at | connected to a | connected to b | connected to c 
-------------------------------------------------------------------------------------------------- 
1001 | 3    | 2015-08-16   | yes   | yes   | yes 
1030 | 1    | 2015-07-03   | no    | no    | yes 
2333 | 2    | 2015-07-02   | yes   | yes   | no 
2502 | 0    |     | no    | no    | no 
3000 | 2    | 2015-09-01   | yes   | no    | yes 
4000 | 1    | 2015-09-01   | no    | no    | yes 
4900 | 1    | 2015-07-03   | no    | yes   | no    

Как мне это сделать?

ответ

4

Во-первых, сделать союз со всеми таблицами:

SELECT user_id, created_at, 1 AS a, 0 AS b, 0 AS c FROM tableA 
UNION 
SELECT user_id, created_at, 0 AS a, 1 AS b, 0 AS c FROM tableB 
UNION 
SELECT user_id, created_at, 0 AS a, 0 AS b, 1 AS c FROM tableC 

затем группу результат этого подзапроса

SELECT user_id, COUNT(user_id), MAX(created_at), MAX(a), MAX(b), MAX(c) 
FROM subquery_above 
GROUP BY user_id 

Это не даст вам нулевой результат, но вы можете добиться того, что с LEFT JOIN в списке пользователей.

3
select 
    user_id, 
    count(p), 
    max(created_at), 
    coalesce(sum((pl = 'a')::int), 0) connected_to_a, 
    coalesce(sum((pl = 'b')::int), 0) connected_to_b, 
    coalesce(sum((pl = 'c')::int), 0) connected_to_c 
from users u 
left join (
    select *, 'a' pl from platform_a 
    union all 
    select *, 'b' pl from platform_b 
    union all 
    select *, 'c' pl from platform_c 
    ) p 
using (user_id) 
group by 1; 

user_id | count | max  | connected_to_a | connected_to_b | connected_to_c 
---------+-------+------------+----------------+----------------+---------------- 
    1001 |  3 | 2015-08-16 |    1 |    1 |    1 
    1030 |  1 | 2015-07-03 |    0 |    0 |    1 
    2333 |  2 | 2015-07-02 |    1 |    1 |    0 
    2502 |  0 |   |    0 |    0 |    0 
    3000 |  2 | 2015-09-01 |    1 |    0 |    1 
    4000 |  1 | 2015-09-01 |    0 |    0 |    1 
    4900 |  1 | 2015-07-03 |    0 |    1 |    0 
(7 rows) 
1

В то время как вы проверить всех пользователей, это обычно быстро агрегировать, прежде чем присоединиться:

SELECT * 
FROM (SELECT user_id FROM users) u -- subquery to clip other columns 
LEFT JOIN (
    SELECT user_id, count(*) AS connections, max(created_at) AS latest_created_at 
     , bool_or(pl = 'a') AS connected_to_a 
     , bool_or(pl = 'b') AS connected_to_b 
     , bool_or(pl = 'c') AS connected_to_c 
    FROM (  SELECT user_id, created_at, 'a'::"char" AS pl FROM platform_a 
     UNION ALL SELECT user_id, created_at, 'b'    FROM platform_b 
     UNION ALL SELECT user_id, created_at, 'c'    FROM platform_b 
    ) p1 
    ) p2 USING (user_id) 
ORDER BY user_id; 

Результат точно по желанию - за исключением того, что connections является NULL вместо «0» в вашем пример. Используйте COALESCE() во внешнем SELECT, если вам нужно его преобразовать. Я этого не сделал, потому что SELECT * так удобен.
Если вы собираетесь перечислить все столбцы во внешнем SELECT, вы можете просто использовать users вместо подзапроса u для клипа других столбцов.

bool_or() - идеальная совокупная функция для работы.

Может быть несколько ссылок на одну платформу. Этот запрос по-прежнему возвращает одну строку для каждого пользователя.

Смежные вопросы