sql >> Databáze >  >> RDS >> PostgreSQL

Použití row_to_json() s vnořenými spojeními

Aktualizace:V PostgreSQL 9.4 se to hodně zlepšilo zavedením to_json , json_build_object , json_object a json_build_array , i když je to podrobné kvůli nutnosti explicitně pojmenovat všechna pole:

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;

Pro starší verze čtěte dále.

Není to omezeno na jeden řádek, je to jen trochu bolestivé. Pomocí AS nelze vytvořit alias složených typů řádků , takže k dosažení efektu musíte použít výraz poddotazu s aliasem nebo CTE:

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;

vyrábí prostřednictvím 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"
    }
  }
}

Budete chtít použít array_to_json(array_agg(...)) když máte vztah 1:mnoho, btw.

Výše uvedený dotaz by v ideálním případě měl být možné napsat jako:

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;

... ale ROW PostgreSQL konstruktor nepřijímá AS aliasy sloupců. Bohužel.

Naštěstí se optimalizují stejně. Porovnejte plány:

  • Verze vnořeného poddotazu; vs
  • Ten druhý vnořený ROW verze konstruktoru s odstraněnými aliasy, takže se spustí

Protože CTE jsou optimalizační ploty, přeformulování verze vnořeného poddotazu tak, aby používala zřetězené CTE (WITH výrazy) nemusí fungovat stejně dobře a nepovede ke stejnému plánu. V tomto případě jste uvízli u ošklivých vnořených poddotazů, dokud nezískáme nějaká vylepšení row_to_json nebo způsob, jak přepsat názvy sloupců v ROW konstruktor více přímo.

Každopádně obecně platí princip, že tam, kde chcete vytvořit objekt json se sloupci a, b, c a přejete si, abyste mohli napsat ilegální syntaxi:

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

místo toho můžete použít skalární poddotazy vracející hodnoty typu řádků:

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

Nebo:

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

Kromě toho mějte na paměti, že můžete vytvořit json hodnoty bez dodatečného uvozování, např. pokud vložíte výstup json_agg v rámci row_to_json , vnitřní json_agg výsledek nebude citován jako řetězec, bude začleněn přímo jako json.

např. v libovolném příkladu:

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

výstup je:

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

Všimněte si, že json_agg produkt, [{"a":1,"b":2}, {"a":1,"b":2}] , nebyl znovu escapován jako text by bylo.

To znamená, že můžete skládat json operacemi pro konstrukci řádků, nemusíte vždy vytvářet extrémně složité složené typy PostgreSQL a poté volat row_to_json na výstupu.



  1. Oracle Database Link – ekvivalent MySQL?

  2. Zkopírujte strukturu tabulky do nové tabulky

  3. Jak odebrat záhlaví sloupců při odesílání výsledků dotazu e-mailem na SQL Server (T-SQL)

  4. Jednoduchý případ použití pro indexy na primárních klíčích