PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Verwendung von row_to_json() mit verschachtelten Joins

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.