2014-07-26 4 views
10

Mam tabeli temp zdefiniowany następująco:Jak ustawić poprawne nazwy atrybutów do zagregowanego wyniku json z klauzulą ​​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 

chciałbym produkować wynik spis group_id i zagregowane do JSON. Jednak kwerendy tak:

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

Produkuje ten wynik:

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"}] 

atrybuty w obiektach json są nazwane f1, f2, f3 zamiast id, name, body wymagane. Wiem, że to jest możliwe do aliasu je prawidłowo za pomocą podzapytania lub wspólnego wyrażenia tabeli, na przykład tak:

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

która produkuje ten wynik:

[{"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"}] 

Ale szczerze mówiąc nie widzę jakikolwiek sposób użycia go w połączeniu z agregacją. czego mi brakuje?

Odpowiedz

15

Do tego nie potrzebujesz tymczasowego stołu ani typu, ale nie jest to piękne.

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

Tu używamy dwa podzapytania - po pierwsze, aby skonstruować zestaw wyników z zaledwie trzech pożądanych kolumn, a następnie zewnętrzną podzapytania, aby ją jako złożonego rowtype.

Nadal będzie działał dobrze.


Aby to zrobić ze składnią mniej brzydki, PostgreSQL musiałby pozwalają ustawić aliasy dla anonimowych rowtypes, jak na poniższym (nieprawidłowy) Składnia:

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

lub my potrzebujemy wariant row_to_json aliasy kolumn, które miały, podobnie jak (znów nieważny):

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

albo/oba byłoby miło, ale nie są obecnie obsługiwane.

+0

Niezbyt elegancka, ale wygląda na dość prostą, w gruncie rzeczy zbyt prosta, aby nawet ją rozważyć, dzięki! – Przemek

0

Cóż, odpowiadając na moje własne pytanie kilka minut po zadaniu, ale znalazłem sposób ... Po prostu nie wiem, że to najlepszy. Rozwiązałem go tworząc typ mieszany:

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

i przepisywanie moje zapytanie dodając obsadę do typu:

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

która wyprodukowała oczekiwany wynik:

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

byłem wystarczy zamieścić bardzo podobne rozwiązanie: to yours po prostu używając tymczasowego stołu

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

Opierając się na @Craig's answer aby uczynić go bardziej elegancki, tutaj kompozyt rowtype zbudowany jest na liście

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

W PostgreSQL 9.4 można wykorzystać json_build_object()from.

Dla przykładu, to działa tak:

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

jest bardziej przyjazny sposób, Postgres nas kocha: 3

1

Jeśli trzeba wszystkie pola z tabeli, a następnie można użyć tej metody:

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