PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Utilizzo di row_to_json() con join nidificati

Aggiornamento:in PostgreSQL 9.4 questo migliora molto con l'introduzione di to_json , json_build_object , json_object e json_build_array , sebbene sia dettagliato a causa della necessità di nominare tutti i campi in modo esplicito:

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;

Per le versioni precedenti, continua a leggere.

Non è limitato a una singola riga, è solo un po' doloroso. Non puoi alias di tipi di riga compositi utilizzando AS , quindi è necessario utilizzare un'espressione di sottoquery con alias o CTE per ottenere l'effetto:

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;

produce, tramite 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"
    }
  }
}

Dovrai usare array_to_json(array_agg(...)) quando hai una relazione 1:molti, a proposito.

La query precedente dovrebbe idealmente essere scritta come:

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;

... ma ROW di PostgreSQL il costruttore non accetta AS alias di colonna. Purtroppo.

Per fortuna, ottimizzano lo stesso. Confronta i piani:

  • La versione nidificata della sottoquery; contro
  • Quest'ultimo ha nidificato ROW versione del costruttore con gli alias rimossi in modo che venga eseguito

Poiché i CTE sono barriere di ottimizzazione, riformulare la versione della sottoquery nidificata per utilizzare CTE concatenati (WITH espressioni) potrebbero non funzionare altrettanto bene e non risulteranno nello stesso piano. In questo caso sei un po' bloccato con brutte sottoquery nidificate finché non otteniamo alcuni miglioramenti a row_to_json o un modo per sovrascrivere i nomi delle colonne in una ROW costruttore in modo più diretto.

Comunque, in generale, il principio è quello dove vuoi creare un oggetto json con colonne a, b, c e vorresti poter semplicemente scrivere la sintassi illegale:

ROW(a, b, c) AS outername(name1, name2, name3)

puoi invece utilizzare sottoquery scalari che restituiscono valori tipizzati per riga:

(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername

Oppure:

(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername

Inoltre, tieni presente che puoi comporre json valori senza virgolette aggiuntive, ad es. se metti l'output di un json_agg all'interno di un row_to_json , il json_agg interno result non verrà citato come una stringa, verrà incorporato direttamente come json.

per esempio. nell'esempio arbitrario:

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
);

l'output è:

{"k1":1,
 "k2":2,
 "k3":[{"a":1,"b":2}, 
 {"a":1,"b":2}]}

Nota che il json_agg prodotto, [{"a":1,"b":2}, {"a":1,"b":2}] , non è stato più eseguito l'escape, come text sarebbe.

Ciò significa che puoi comporre json per costruire righe, non è sempre necessario creare tipi compositi PostgreSQL estremamente complessi, quindi chiamare row_to_json sull'uscita.