Aktualizacja:W PostgreSQL 9.4 znacznie się to poprawia dzięki wprowadzeniu to_json
, json_build_object
, json_object
i json_build_array
, choć jest to gadatliwe ze względu na konieczność wyraźnego nazwania wszystkich pól:
select
json_build_object(
'id', u.id,
'name', u.name,
'email', u.email,
'user_role_id', u.user_role_id,
'user_role', json_build_object(
'id', ur.id,
'name', ur.name,
'description', ur.description,
'duty_id', ur.duty_id,
'duty', json_build_object(
'id', d.id,
'name', d.name
)
)
)
from users u
inner join user_roles ur on ur.id = u.user_role_id
inner join role_duties d on d.id = ur.duty_id;
W przypadku starszych wersji czytaj dalej.
Nie ogranicza się do jednego rzędu, to po prostu trochę bolesne. Nie można aliasować złożonych typów wierszy za pomocą AS
, więc aby uzyskać ten efekt, musisz użyć aliasowanego wyrażenia podzapytania lub CTE:
select row_to_json(row)
from (
select u.*, urd AS user_role
from users u
inner join (
select ur.*, d
from user_roles ur
inner join role_duties d on d.id = ur.duty_id
) urd(id,name,description,duty_id,duty) on urd.id = u.user_role_id
) row;
produkuje, za pośrednictwem http://jsonprettyprint.com/:
{
"id": 1,
"name": "Dan",
"email": "[email protected]",
"user_role_id": 1,
"user_role": {
"id": 1,
"name": "admin",
"description": "Administrative duties in the system",
"duty_id": 1,
"duty": {
"id": 1,
"name": "Script Execution"
}
}
}
Będziesz chciał użyć array_to_json(array_agg(...))
kiedy masz związek 1:wielu, przy okazji.
Najlepiej byłoby, gdyby powyższe zapytanie można było zapisać jako:
select row_to_json(
ROW(u.*, ROW(ur.*, d AS duty) AS user_role)
)
from users u
inner join user_roles ur on ur.id = u.user_role_id
inner join role_duties d on d.id = ur.duty_id;
... ale ROW
PostgreSQLa Konstruktor nie akceptuje AS
aliasy kolumn. Niestety.
Na szczęście optymalizują to samo. Porównaj plany:
- Wersja zagnieżdżonego podzapytania; vs
- Ostatni zagnieżdżony
ROW
wersja konstruktora z usuniętymi aliasami, więc wykonuje się
Ponieważ CTE są ograniczeniami optymalizacji, przeformułowanie zagnieżdżonej wersji podzapytania w celu użycia połączonych CTE (WITH
wyrażenia) mogą nie działać tak dobrze i nie będą skutkować tym samym planem. W tym przypadku utkniesz z brzydkimi zagnieżdżonymi podzapytaniami, dopóki nie uzyskamy pewnych ulepszeń w row_to_json
lub sposób na nadpisanie nazw kolumn w ROW
Konstruktor bardziej bezpośrednio.
W każdym razie generalnie zasada jest taka, że tam, gdzie chcesz utworzyć obiekt json z kolumnami a, b, c
, i chcesz po prostu napisać nieprawidłową składnię:
ROW(a, b, c) AS outername(name1, name2, name3)
możesz zamiast tego użyć podzapytań skalarnych zwracających wartości wpisane w wiersze:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
Lub:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
Dodatkowo pamiętaj, że możesz komponować json
wartości bez dodatkowego cytowania, np. jeśli umieścisz wyjście json_agg
w row_to_json
, wewnętrzny json_agg
wynik nie będzie cytowany jako ciąg, zostanie dołączony bezpośrednio jako json.
np. w dowolnym przykładzie:
SELECT row_to_json(
(SELECT x FROM (SELECT
1 AS k1,
2 AS k2,
(SELECT json_agg( (SELECT x FROM (SELECT 1 AS a, 2 AS b) x) )
FROM generate_series(1,2) ) AS k3
) x),
true
);
wyjście to:
{"k1":1,
"k2":2,
"k3":[{"a":1,"b":2},
{"a":1,"b":2}]}
Zauważ, że json_agg
produkt, [{"a":1,"b":2}, {"a":1,"b":2}]
, nie został ponownie zmieniony, ponieważ text
byłoby.
Oznacza to, że możesz komponować operacje json do konstruowania wierszy, nie zawsze musisz tworzyć bardzo złożone typy złożone PostgreSQL, a następnie wywoływać row_to_json
na wyjściu.