sql >> Databasteknik >  >> RDS >> PostgreSQL

Använder row_to_json() med kapslade kopplingar

Uppdatering:I PostgreSQL 9.4 förbättras detta mycket med introduktionen av to_json , json_build_object , json_object och json_build_array , även om det är utförligt på grund av behovet av att namnge alla fält uttryckligen:

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 äldre versioner, läs vidare.

Det är inte begränsat till en enda rad, det är bara lite smärtsamt. Du kan inte alias sammansatta radtyper med AS , så du måste använda ett alias subquery-uttryck eller CTE för att uppnå effekten:

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;

producerar, via 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"
    }
  }
}

Du kommer att vilja använda array_to_json(array_agg(...)) när man har ett 1:många förhållande, btw.

Ovanstående fråga bör helst kunna skrivas som:

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;

... men PostgreSQL:s ROW konstruktorn accepterar inte AS kolumnalias. Tyvärr.

Tack och lov optimerar de detsamma. Jämför planerna:

  • Den kapslade versionen av underfrågan; vs
  • Den senare kapslade ROW konstruktorversionen med aliasen borttagna så att den körs

Eftersom CTE:er är optimeringsstängsel, omformulera den kapslade underfrågaversionen till att använda kedjade CTE:er (WITH expressions) kanske inte fungerar lika bra och kommer inte att resultera i samma plan. I det här fallet har du typ fastnat med fula kapslade underfrågor tills vi får några förbättringar av row_to_json eller ett sätt att åsidosätta kolumnnamnen i en ROW konstruktör mer direkt.

Hur som helst, generellt sett är principen att där du vill skapa ett json-objekt med kolumner a, b, c , och du önskar att du bara kunde skriva den olagliga syntaxen:

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

du kan istället använda skalära underfrågor som returnerar radtypade värden:

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

Eller:

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

Tänk dessutom på att du kan skapa json värden utan ytterligare citat, t.ex. om du sätter utdata från en json_agg inom en row_to_json , den inre json_agg resultatet kommer inte att citeras som en sträng, det kommer att inkorporeras direkt som json.

t.ex. i det godtyckliga exemplet:

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

utgången är:

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

Observera att json_agg produkt, [{"a":1,"b":2}, {"a":1,"b":2}] , har inte escapets igen, som text skulle vara.

Det betyder att du kan skriva json-operationer för att konstruera rader, du behöver inte alltid skapa enormt komplexa PostgreSQL-komposittyper och sedan anropa row_to_json på utgången.



  1. MySQL ATAN() Funktion – Returnera bågtangenten för ett värde (eller värden)

  2. Hur kastar jag en sträng till heltal och har 0 vid fel i casten med PostgreSQL?

  3. Hur man skapar och kör MySQL-lagrade funktioner och procedurer

  4. Hur man får input från användaren vid körning