Wählen Sie Spalten in json_agg aus


21

Ich habe eine Frage wie:

SELECT a.id, a.name, json_agg(b.*) as "item"
  FROM a
  JOIN b ON b.item_id = a.id
 GROUP BY a.id, a.name;

Wie kann ich die Spalten auswählen , bso habe ich nicht b.item_idin der JSON - Objekt?

Ich habe darüber gelesen ROW, aber es gibt ein JSON-Objekt wie folgt zurück:

{"f1": "Foo", "f2": "Bar"}

Ich müsste das JSON-Objekt neu zuordnen, sobald es abgerufen wird, um mit den richtigen Spaltenschlüsseln übereinzustimmen. Ich möchte das vermeiden und die ursprünglichen Spaltennamen beibehalten.

Antworten:


50

Leider gibt es in der SQL-Syntax keine Bestimmung, die besagt, dass "alle Spalten außer dieser einen Spalte" . Sie können Ihr Ziel erreichen, indem Sie die verbleibende Liste der Spalten in einem Ausdruck vom Zeilentyp ausschreiben :

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

Das ist die Abkürzung für die explizitere Form: . ROW(b.col1, b.col2, b.col3)

Allerdings Spaltennamen sind nicht in Reihe-Typ Ausdrücke erhalten. Auf diese Weise erhalten Sie generische Schlüsselnamen im JSON-Objekt. Ich sehe 3 Optionen, um die ursprünglichen Spaltennamen beizubehalten:

1. Auf registrierten Typ umwandeln

Umwandlung in einen bekannten (registrierten) Zeilentyp. Ein Typ wird für jede vorhandene Tabelle oder Sicht oder mit einer expliziten CREATE TYPEAnweisung registriert . Sie können eine temporäre Tabelle für eine Ad-hoc-Lösung verwenden (gültig für die Dauer der Sitzung):

CREATE TEMP TABLE x (col1 int, col2 text, col3 date);  -- use adequate data types!

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)::x) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

2. Verwenden Sie eine Unterauswahl

Verwenden Sie eine Unterauswahl, um eine abgeleitete Tabelle zu erstellen und auf die gesamte Tabelle zu verweisen . Dies trägt auch Spaltennamen. Es ist ausführlicher, aber Sie benötigen keinen registrierten Typ:

SELECT a.id, a.name
     , json_agg((SELECT x FROM (SELECT b.col1, b.col2, b.col3) AS x)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

3. json_build_object()in Postgres 9.4 oder höher

SELECT a.id, a.name
     , json_agg(json_build_object('col1', b.col1, 'col2', b.col2, 'col3', b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

Verbunden:

Ähnliches gilt für jsonbdie jeweiligen Funktionen jsonb_agg()und jsonb_build_object().

Für Postgres 9.5 oder später auch sehen a_horse Antwort mit einer neuen kürzerer Syntaxvariante: Postgres hinzugefügt , um den Minus-Operator -fürjsonb sagen „alle Tasten außer diesen einem Schlüssel“ .
Da Postgres 10 "außer mehreren Schlüsseln" mit dem gleichen Operator implementiert ist, wird text[]als 2. Operand - wie mlt - kommentiert.


1
> Oder mehr Tasten Beachten Sie, dass json (b) -Text [] verfügbar ist, ausgehend von 10.
mlt

Die Lösung 3 hat für mich wie ein Zauber gewirkt!
Luiz Fernando da Silva

17

Ab 9.6 können Sie einfach -einen Schlüssel aus einem JSONB entfernen:

SELECT a.id, a.name, jsonb_agg(to_jsonb(b) - 'item_id') as "item"
FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

to_jsonb(b)konvertiert die gesamte Zeile und entfernt - 'item_id'dann den Schlüssel mit dem Namen, item_iddessen Ergebnis dann aggregiert wird.


Diese neuen Funktionen scheinen das zu sein, worauf das OP gehofft hatte. Ich habe meiner Antwort einen Link hinzugefügt.
Erwin Brandstetter

Als ich die Subselect-Variante ausprobierte, bekam ich einen Fehler in Bezug auf die json_aggFunktion:function json_agg(record) does not exist
fraxture

@fraxture: dann benutzt du nicht Postgres 9.6
a_horse_with_no_name

In der Tat war das das Problem. Gibt es eine Möglichkeit, Spalten in Version 9.2 zu filtern?
Fraxture

8

Sie können dies mithilfe von Unterabfragen auch ohne Gruppierung durchführen

SELECT 
  a.id, a.name, 
  ( 
    SELECT json_agg(item)
    FROM (
      SELECT b.c1 AS x, b.c2 AS y 
      FROM b WHERE b.item_id = a.id
    ) item
  ) AS items
FROM a;

kehrt zurück

{
  id: 1,
  name: "thing one",
  items:[
    { x: "child1", y: "child1 col2"},
    { x: "child2", y: "child2 col2"}
  ]
}

Dieser Artikel von John Atten ist wirklich interessant und hat mehr Details


2

Ich habe festgestellt, dass es am besten ist, den JSON zu erstellen und dann zu aggregieren. z.B

with base as (
select a, b, ('{"ecks":"' || to_json(x) || '","wai":"' || to_json(y) || '","zee":"' || to_json(z) || '"}"')::json c
) select (a, b, array_to_json(array_agg(c)) as c)

Beachten Sie, dass dies als Unterabfrage durchgeführt werden kann, wenn Sie CTEs nicht mögen (oder Leistungsprobleme haben, weil Sie sie verwenden).

Beachten Sie auch, dass es von Vorteil sein kann, eine Funktion zu erstellen, die die Schlüssel-Wert-Paare für Sie umschließt, damit der Code übersichtlicher aussieht. Sie würden Ihre Funktion (zum Beispiel) übergeben 'ecks', 'x'und es würde zurückkehren "ecks": "x".


1

Es gibt zwar immer noch keine Möglichkeit, die Auswahl aller Spalten bis auf ein Bit zu json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))ändern , aber Sie können damit ein JSON-Array von JSONs im Format abrufen {"col_1":"col_1 value", ...}.

Die Abfrage würde also ungefähr so ​​aussehen:

SELECT a.id, a.name, json_agg(to_json(b.col_1,b.col_2,b.col_3...)) as item
  FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

und würde Zeilen zurückgeben als:

id, name, item
8, the_name, [{"col_1":"value_1","col_2":"value_2","col_3":"value_3"...}, {"col_1":"value_1.2","col_2":"value_2.2","col_3":"value_3.2"...},...]
9, the_next_name, [{"col_1":"value_1.3","col_2":"value_2.3","col_3":"value_3.3"...},   {"col_1":"value_1.4","col_2":"value_2.4","col_3":"value_3.4"...},...]
...

(Ich bin jetzt auf Postgres 9.5.3 und nicht 100% sicher, wann diese Unterstützung hinzugefügt wurde.)


1

Sie können verwendet werden, json_build_objectwie dies

SELECT 
  a.id, 
  a.name,
  json_agg(json_build_object('col1', b.col1, 'col2', b.col2) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.