2015-12-08 2 views
10

Я хотел бы объединить два столбца в один «массив» при группировке.Postgres - совокупность двух столбцов на один элемент

Пусть таблица так:

friends_map: 
================================= 
user_id friend_id confirmed 
================================= 
1   2   true 
1   3   false 
2   1   true 
2   3   true 
1   4   false 

Я хотел бы, чтобы выбрать из этой таблицы и группы по user_id и получить friend_id и утвержден в качестве объединенного значения, разделенные запятой.

В настоящее время у меня есть это:

SELECT user_id, array_agg(friend_id) as friends, array_agg(confirmed) as confirmed 
FROM friend_map 
WHERE user_id = 1 
GROUP BY user_id 

который получает меня:

================================= 
user_id friends  confirmed 
================================= 
1   [2,3,4]  [t, f, f] 

Как я могу получить:

================================= 
user_id friends  
================================= 
1   [ [2,t], [3,f], [4,f] ] 
+0

В некотором смысле , это то, что у вас было, когда вы начали. :) –

+0

Но то, что я ищу, группируется под одним именем переменной. Это всего лишь пример, это действительно часть большого запроса на соединение, в котором содержится больше таблиц и данных. – SoluableNonagon

ответ

12

Вы можете сцепить значения вместе перед подачей их в array_agg() Функция:

SELECT user_id, array_agg('[' || friend_id || ',' || confirmed || ']') as friends 
FROM friends_map 
WHERE user_id = 1 
GROUP BY user_id 

Демо: SQL Fiddle

+1

Обратите внимание, что это строение строк типа '' [2, t] '', а не вложенных массивов. Фактические массивы Postgres не могут содержать значения разных типов (например, integer и boolean). –

+0

@ DanielLyons Хорошая мысль, я не привык думать о хранении вложенных массивов, подобных этому в rdbms, так что думал строки. –

+0

Все в порядке; нет правильного способа сделать неправильную вещь. :) –

9

В Postgres 9,5 вы можете получить массив массивов текста:

SELECT user_id, array_agg(array[friend_id::text, confirmed::text]) 
FROM friend_map 
WHERE user_id = 1 
GROUP BY user_id; 

user_id |   array_agg    
---------+-------------------------------- 
     1 | {{2,true},{3,false},{4,false}} 
(1 row) 

или массив массивов междунар:

SELECT user_id, array_agg(array[friend_id, confirmed::int]) 
FROM friend_map 
WHERE user_id = 1 
GROUP BY user_id; 

user_id |  array_agg  
---------+--------------------- 
     1 | {{2,1},{3,0},{4,0}} 
(1 row) 
+0

Просто вопрос, какую версию вы используете? 'array_agg' не должен собирать' text [] 'или' integer [] '... – Eggplant

+0

Ops! PostgreSQL 9.5beta2 – klin

+0

Aha! Вы меня там нашли, мне было интересно, что это за колдовство *: 9.5 еще не выпущено, поэтому я предполагаю, что OP ищет какое-то готовое к производству решение. – Eggplant

18
SELECT user_id, array_agg((friend_id, confirmed)) as friends 
FROM friend_map 
WHERE user_id = 1 
GROUP BY user_id 

user_id |   array_agg    
--------+-------------------------------- 
     1 | {"(2,true)","(3,false)","(4,false)"} 
+1

очень простое решение, спасибо. – SoluableNonagon

10

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

SELECT user_id, json_agg(json_build_array(friend_id, confirmed)) AS friends 
    FROM friends_map 
    WHERE user_id = 1 
    GROUP BY user_id 

Или использовать некоторые key : value пары, так как JSON допускает, что, таким образом, на выходе будет больше семантический если вам нравится:

SELECT user_id, json_agg(json_build_object(
     'friend_id', friend_id, 
     'confirmed', confirmed 
    )) AS friends 
    FROM friends_map 
    WHERE user_id = 1 
    GROUP BY user_id; 
+0

Это, безусловно, правильный ответ, вы получаете хороший результат как json (это, вероятно, все, чего мы все хотим в любом случае), и это выглядит как [[1990, «pm25»], [1995, «pm25»], [2000, "PM25"]] – chrismarx

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