sql >> Database >  >> RDS >> PostgreSQL

Row_to_json() gebruiken met geneste joins

Update:in PostgreSQL 9.4 is dit veel verbeterd met de introductie van to_json , json_build_object , json_object en json_build_array , hoewel het uitgebreid is vanwege de noodzaak om alle velden expliciet een naam te geven:

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;

Lees verder voor oudere versies.

Het is niet beperkt tot een enkele rij, het is gewoon een beetje pijnlijk. U kunt samengestelde rijtypen geen aliassen gebruiken met AS , dus u moet een aliased subquery-expressie of CTE gebruiken om het effect te bereiken:

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;

produceert, 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"
    }
  }
}

U wilt array_to_json(array_agg(...)) gebruiken als je een 1:veel-relatie hebt, btw.

De bovenstaande vraag zou idealiter kunnen worden geschreven als:

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;

... maar PostgreSQL's ROW constructor accepteert AS niet kolom aliassen. Helaas.

Gelukkig optimaliseren ze hetzelfde. Vergelijk de abonnementen:

  • De geneste subquery-versie; vs
  • De laatste geneste ROW constructorversie met de aliassen verwijderd zodat het wordt uitgevoerd

Omdat CTE's optimalisatiehekken zijn, is het herformuleren van de geneste subqueryversie om geketende CTE's te gebruiken (WITH expressions) presteren mogelijk niet zo goed en resulteren niet in hetzelfde plan. In dit geval zit je een beetje vast met lelijke geneste subquery's totdat we enkele verbeteringen krijgen in row_to_json of een manier om de kolomnamen in een ROW te overschrijven constructor directer.

Hoe dan ook, in het algemeen is het principe dat waar je een json-object wilt maken met kolommen a, b, c , en je zou willen dat je gewoon de illegale syntaxis kon schrijven:

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

u kunt in plaats daarvan scalaire subquery's gebruiken die rijgetypte waarden retourneren:

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

Of:

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

Houd er bovendien rekening mee dat u json . kunt samenstellen waarden zonder aanvullende aanhalingstekens, b.v. als je de uitvoer van een json_agg binnen een row_to_json , de innerlijke json_agg resultaat wordt niet als een tekenreeks geciteerd, het wordt direct als json opgenomen.

bijv. in het willekeurige voorbeeld:

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

de uitvoer is:

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

Merk op dat de json_agg product, [{"a":1,"b":2}, {"a":1,"b":2}] , is niet opnieuw ontsnapt, als text zou zijn.

Dit betekent dat je kunt componeren json-bewerkingen om rijen te construeren, hoeft u niet altijd enorm complexe PostgreSQL-composiettypen te maken en vervolgens row_to_json aan te roepen op de uitgang.



  1. MySQL:#126 - Onjuist sleutelbestand voor tabel

  2. ORA-16789:standby-logbestanden voor opnieuw uitvoeren onjuist geconfigureerd

  3. Hoe een RDS Postgres-database pg_dumpen?

  4. Verbinding maken met een externe MySQL-server met behulp van PHP