Update:In PostgreSQL 9.4 hat sich dies mit der Einführung von to_json
erheblich verbessert , json_build_object
, json_object
und json_build_array
, obwohl es aufgrund der Notwendigkeit, alle Felder explizit zu benennen, ausführlich ist:
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;
Für ältere Versionen lesen Sie weiter.
Es ist nicht auf eine einzelne Reihe beschränkt, es ist nur ein bisschen schmerzhaft. Sie können zusammengesetzte Zeilentypen nicht mit AS
aliasieren , also müssen Sie einen Alias-Unterabfrageausdruck oder CTE verwenden, um den Effekt zu erzielen:
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;
produziert, über 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"
}
}
}
Sie sollten array_to_json(array_agg(...))
verwenden wenn Sie übrigens eine 1:n-Beziehung haben.
Die obige Abfrage sollte idealerweise wie folgt geschrieben werden können:
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;
... aber PostgreSQLs ROW
Der Konstruktor akzeptiert AS
nicht Spaltenaliase. Leider.
Glücklicherweise optimieren sie dasselbe. Vergleichen Sie die Pläne:
- Die Version der verschachtelten Unterabfrage; gegen
- Letzteres verschachtelte
ROW
Konstruktorversion mit entfernten Aliasen, damit sie ausgeführt wird
Da CTEs Optimierungszäune sind, ist die Umformulierung der verschachtelten Unterabfrageversion zur Verwendung verketteter CTEs (WITH
Ausdrücke) werden möglicherweise nicht so gut ausgeführt und führen nicht zu demselben Plan. In diesem Fall stecken Sie mit hässlichen verschachtelten Unterabfragen fest, bis wir einige Verbesserungen an row_to_json
erhalten oder eine Möglichkeit, die Spaltennamen in einer ROW
zu überschreiben Konstruktor direkter.
Wie auch immer, im Allgemeinen ist das Prinzip, dass Sie ein JSON-Objekt mit den Spalten a, b, c
erstellen möchten , und Sie wünschten, Sie könnten einfach die illegale Syntax schreiben:
ROW(a, b, c) AS outername(name1, name2, name3)
Sie können stattdessen skalare Unterabfragen verwenden, die zeilenartige Werte zurückgeben:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
Oder:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
Denken Sie außerdem daran, dass Sie json
erstellen können Werte ohne zusätzliches Anführungszeichen, z. wenn Sie die Ausgabe eines json_agg
setzen innerhalb eines row_to_json
, das innere json_agg
Das Ergebnis wird nicht als Zeichenfolge in Anführungszeichen gesetzt, sondern direkt als json eingebunden.
z.B. im willkürlichen Beispiel:
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
);
die Ausgabe ist:
{"k1":1,
"k2":2,
"k3":[{"a":1,"b":2},
{"a":1,"b":2}]}
Beachten Sie, dass json_agg
Produkt, [{"a":1,"b":2}, {"a":1,"b":2}]
, wurde nicht erneut maskiert, als text
wäre.
Das bedeutet, dass Sie komponieren können json-Operationen zum Erstellen von Zeilen verwenden, müssen Sie nicht immer äußerst komplexe zusammengesetzte PostgreSQL-Typen erstellen und dann row_to_json
aufrufen am Ausgang.