So verwandeln Sie ein JSON-Array in Zeilen in Postgres

75

Ich habe ein JSON-Array in meiner Postgres-Datenbank gespeichert. Der json sieht so aus:

    [
        {
            "operation": "U",
            "taxCode": "1000",
            "description": "iva description",
            "tax": "12"
        },
        {
            "operation": "U",
            "taxCode": "1001",
            "description": "iva description",
            "tax": "12"
        },
        {
            "operation": "U",
            "taxCode": "1002",
            "description": "iva description",
            "tax": "12"
        }
    ]

Jetzt muss ich das Array so auswählen, dass sich jedes Element in einer anderen Zeile des Abfrageergebnisses befindet. Die von mir ausgeführte SELECT-Anweisung muss die Daten also folgendermaßen zurückgeben:

 data
--------------------------------------------------------------------------------------
 { "operation": "U", "taxCode": "1000", "description": "iva description", "tax":"12"}
 { "operation": "U", "taxCode": "1001", "description": "iva description", "tax":"12"}
 { "operation": "U", "taxCode": "1002", "description": "iva description", "tax":"12"}

Ich habe versucht, die unnest()Funktion zu verwenden

SELECT unnest(json_data::json)
FROM my_table

aber es akzeptiert den jsonbTyp nicht

k4ppa
quelle
10
unnest()ist für die Array-Typen von PostgreSQL. Verwenden Sie json_array_elements(json) (9.3+), jsonb_array_elements(jsonb)(9.4+) oder json[b]_array_elements_text(json[b])(9.4+)
pozs
Vielen Dank. Wenn Sie die Frage stellen, kann ich sie annehmen.
k4ppa

Antworten:

73

Ich poste die Antwort, die ursprünglich von pozs geschrieben wurde, im Kommentarbereich.

unnest() ist für die Array-Typen von PostgreSQL.

Stattdessen kann eine der folgenden Funktionen verwendet werden:

  • json_array_elements(json) (9,3+)
  • jsonb_array_elements(jsonb) (9,4+)
  • json[b]_array_elements_text(json[b]) (9,4+)

Beispiel :

select * from json_array_elements('[1,true, [2,false]]')

Ausgabewert

 -------------
 | 1         |
 -------------
 | true      |
 -------------
 | [2,false] |
 -------------

Hier finden Sie die Dokumentation zu Version 9.4.

k4ppa
quelle
Erwähnenswert ist auch, dass die Funktionen mit dem "_text" die nervigen Anführungszeichen aus JSON-Strings entfernen
Roman M
44

Schwierigeres Beispiel:

Angenommen, Sie haben eine Tabelle mit Zeilen, die jeweils ein jsonb-Array enthalten, und Sie möchten alle diese Arrays aufteilen (oder unnest) und einige aggregierte Berechnungen für die darin enthaltenen Datensätze durchführen.

Tabelle (lass es sein categories):

 id | specifics (jsonb)
-----------------------------------------------------------------------------------
  1 | [{"name": "Brand", "required": true}, {"name": "Color", "required": false}]
  2 | [{"name": "Brand", "required": false}, {"name": "Color", "required": false}]

Wenn Sie also zählen möchten, wie viele erforderliche Details Sie haben, müssen Sie folgende Abfrage verwenden:

SELECT specs.name, COUNT(*) AS total
FROM 
  categories, 
  jsonb_to_recordset(categories.specifics) AS specs(name jsonb, required boolean)
WHERE 
  specs.required = TRUE
  -- AND any other restrictions you need
GROUP BY specs.name
ORDER BY total DESC;

Hier FROM x, function(x.column)ist eine verkürzte Form einer lateralen Verknüpfung, die effektiv jede Zeile categoriesmit einer virtuellen Tabelle verbindet, die durch eine jsonb_to_recordsetFunktion aus dem jsonb-Array in derselben Zeile erstellt wurde.

Und das Ergebnis wird sein:

 name  | total
---------------
 Brand |     1

Link zu DB Fiddle: https://www.db-fiddle.com/f/c4xZcEgg9dsPVDtE7Keovv/0

Envek
quelle
Tolles Beispiel für die Verwendung von seitlichen Verbindungen
Lerner
37

Ich würde vorschlagen, in Ihrem Fall den Befehl json_to_recordset zu verwenden. Ihr SQL sollte dann sein:

select *
from json_to_recordset('[{"operation":"U","taxCode":1000},{"operation":"U","taxCode":10001}]')
as x("operation" text, "taxCode" int);

Die Ausgabe ist:

------------------------
|   |operation|taxCode |
------------------------
| 1 |   "U"   |   1000 |
------------------------
| 2 |   "U"   |  10001 |
------------------------

Die Spalten (oder JSON-Schlüssel) des Beispiels können frei weiter erweitert werden.

user2080851
quelle
Wie ist es auf Objekt möglich? bedeutet, dass ich select * from json_to_recordset ('{"operation": "U", "taxCode": 1000}') als x ("operation" text, "taxCode" int) konvertieren möchte; dies aber json_to_recordset funktioniert nicht
Sherin Green