2014-07-26 5 views
10

Ho una tabella temp definito così:Come impostare i nomi degli attributi corretti su un risultato aggregato json con la clausola GROUP BY?

id | name | body | group_id 
------------------------------- 
1 | test_1 | body_1 | 1 
2 | test_2 | body_2 | 1 
3 | test_3 | body_3 | 2 
4 | test_4 | body_4 | 2 

desidero produrre un risultato raggruppati per group_id e aggregati a json. Tuttavia, query come questa:

SELECT group_id, json_agg(ROW(id, name, body)) FROM temp 
GROUP BY group_id; 

Produce questo risultato:

1;[{"f1":1,"f2":"test_1","f3":"body_1"}, 
    {"f1":2,"f2":"test_2","f3":"body_2"}] 
2;[{"f1":3,"f2":"test_3","f3":"body_3"}, 
    {"f1":4,"f2":"test_4","f3":"body_4"}] 

Gli attributi negli oggetti JSON sono chiamati f1, f2, f3 invece di id, name, body come richiesto. So che è possibile alias correttamente utilizzando un subquery o di un'espressione di tabella comune, per esempio come questo:

SELECT json_agg(r.*) FROM (
    SELECT id, name, body FROM temp 
) r; 

che produce questo risultato:

[{"id":1,"name":"test_1","body":"body_1"}, 
{"id":2,"name":"test_2","body":"body_2"}, 
{"id":3,"name":"test_3","body":"body_3"}, 
{"id":4,"name":"test_4","body":"body_4"}] 

ma onestamente non vedo in qualsiasi modo come usarlo in combinazione con l'aggregazione. Cosa mi manca?

risposta

15

Non è necessario un tavolo temporaneo o tipo per questo, ma non è bello.

SELECT json_agg(row_to_json((SELECT r FROM (SELECT id, name, body) r))) 
FROM t 
GROUP BY group_id; 

Qui, usiamo due sottointerrogazioni - prime, per costruire un set di risultati con solo le tre colonne desiderate, quindi sottoquery esterna per ottenere come rowtype composito.

Continuerà a funzionare correttamente.


Per questo essere fatto con la sintassi meno brutta, PostgreSQL avrebbe bisogno di consente di impostare alias per rowtypes anonimi, come il seguente (non valido) sintassi:

SELECT json_agg(row_to_json(ROW(id, name, body) AS (id, name, body))) 
FROM t 
GROUP BY group_id; 

o avremmo bisogno di un variante di row_to_json che ha preso alias di colonna, come il (di nuovo non valido):

SELECT json_agg(row_to_json(ROW(id, name, body), ARRAY['id', 'name', 'body'])) 
FROM t 
GROUP BY group_id; 

uno/entrambi i quali sarebbe bello, ma non sono attualmente supportati.

+0

Non proprio elegante ma sembra abbastanza semplice, in realtà troppo semplice per me anche considerare questo in primo luogo, grazie! – Przemek

0

Bene, rispondendo alla mia domanda un paio di minuti dopo aver chiesto, ma ho trovato un modo ... Solo non so che è il migliore. Ho risolto con la creazione di un tipo composito:

CREATE TYPE temp_type AS (
    id bigint, 
    name text, 
    body text 
); 

e riscrittura mia domanda con l'aggiunta di un cast al tipo:

SELECT group_id, json_agg(CAST(ROW(id, name, body) AS temp_type)) FROM temp 
GROUP BY group_id; 

che ha prodotto il risultato atteso:

1;[{"id":1,"name":"test_1","body":"body_1"}, 
    {"id":2,"name":"test_2","body":"body_2"}] 
2;[{"id":3,"name":"test_3","body":"body_3"}, 
    {"id":4,"name":"test_4","body":"body_4"}] 
2

ero pubblicheremo una soluzione molto simile to yours usando solo una tabella temporanea

create table t (
    id int, 
    name text, 
    body text, 
    group_id int 
); 
insert into t (id, name, body, group_id) values 
(1, 'test_1', 'body_1', 1), 
(2, 'test_2', 'body_2', 1), 
(3, 'test_3', 'body_3', 2), 
(4, 'test_4', 'body_4', 2); 

create temporary table tt(
    id int, 
    name text, 
    body text 
); 

select group_id, json_agg(row(id, name, body)::tt) 
from t 
group by group_id; 
group_id |     json_agg     
----------+--------------------------------------------- 
     1 | [{"id":1,"name":"test_1","body":"body_1"}, + 
      | {"id":2,"name":"test_2","body":"body_2"}] 
     2 | [{"id":3,"name":"test_3","body":"body_3"}, + 
      | {"id":4,"name":"test_4","body":"body_4"}] 
4

Sulla @Craig's answer per renderlo più elegante, ecco la rowtype composito è costruito in from lista

select json_agg(row_to_json(s)) 
from 
    t 
    cross join lateral 
    (select id, name, body) s 
group by group_id; 
             json_agg          
-------------------------------------------------------------------------------------- 
[{"id":1,"name":"test_1","body":"body_1"}, {"id":2,"name":"test_2","body":"body_2"}] 
[{"id":3,"name":"test_3","body":"body_3"}, {"id":4,"name":"test_4","body":"body_4"}] 
21

In Postgres 9.4 è possibile utilizzare json_build_object().

Per esempio, funziona come:

SELECT group_id, 
     json_agg(json_build_object('id', id, 'name', name, 'body', body)) 
FROM temp 
GROUP BY group_id; 

questo è un modo più amichevole, Postgres ci ama: 3

1

Se avete bisogno di tutti i campi da tavolo, allora si può utilizzare questo approccio:

SELECT 
    group_id, json_agg(temp.*) 
FROM 
    temp 
GROUP BY 
    group_id;