Abfrage nach Array-Elementen innerhalb des JSON-Typs


118

Ich versuche, den jsonTyp in PostgreSQL 9.3 zu testen .
Ich habe eine jsonSpalte datain einer Tabelle namens aufgerufen reports. Der JSON sieht ungefähr so ​​aus:

{
  "objects": [
    {"src":"foo.png"},
    {"src":"bar.png"}
  ],
  "background":"background.png"
}

Ich möchte die Tabelle nach allen Berichten abfragen, die mit dem Wert 'src' im Array 'objects' übereinstimmen. Ist es beispielsweise möglich, die Datenbank nach allen übereinstimmenden Berichten abzufragen 'src' = 'foo.png'? Ich habe erfolgreich eine Abfrage geschrieben, die mit Folgendem übereinstimmt "background":

SELECT data AS data FROM reports where data->>'background' = 'background.png'

Aber da "objects"es eine Reihe von Werten gibt, kann ich scheinbar nichts schreiben, was funktioniert. Ist es möglich, die Datenbank nach allen übereinstimmenden Berichten abzufragen 'src' = 'foo.png'? Ich habe diese Quellen durchgesehen, kann sie aber immer noch nicht verstehen:

Ich habe auch solche Dinge ausprobiert, aber ohne Erfolg:

SELECT json_array_elements(data->'objects') AS data from reports
WHERE  data->>'src' = 'foo.png';

Ich bin kein SQL-Experte, daher weiß ich nicht, was ich falsch mache.

Antworten:


214

json in Postgres 9.3+

Deaktivieren Sie das JSON-Array mit der Funktion json_array_elements()in einem lateralen Join in der FROMKlausel und testen Sie seine Elemente:

WITH reports(data) AS (
   VALUES ('{"objects":[{"src":"foo.png"}, {"src":"bar.png"}]
           , "background":"background.png"}'::json)
   ) 
SELECT *
FROM   reports r, json_array_elements(r.data#>'{objects}') obj
WHERE  obj->>'src' = 'foo.png';

Der CTE ( WITHAbfrage) ersetzt nur eine Tabelle reports.
Oder, das entspricht nur für eine einzige Ebene der Verschachtelung:

SELECT *
FROM   reports r, json_array_elements(r.data->'objects') obj
WHERE  obj->>'src' = 'foo.png';

->>, ->Und #>Operatoren im Handbuch erläutert.

Beide Abfragen verwenden ein implizites JOIN LATERAL.

SQL Fiddle.

Eng verwandte Antwort:

jsonb in Postgres 9.4+

Verwenden Sie das Äquivalent jsonb_array_elements().

Besser noch, verwenden Sie den neuen Operator "enthält" @>(am besten in Kombination mit einem passenden GIN-Index für den Ausdruck data->'objects'):

CREATE INDEX reports_data_gin_idx ON reports
USING gin ((data->'objects') jsonb_path_ops);

SELECT * FROM reports WHERE data->'objects' @> '[{"src":"foo.png"}]';

Da der Schlüssel objectsein JSON- Array enthält , müssen wir die Struktur im Suchbegriff anpassen und das Array-Element auch in eckige Klammern setzen. Löschen Sie die Array-Klammern, wenn Sie einen einfachen Datensatz durchsuchen.

Detaillierte Erklärung und weitere Optionen:


1
@pacothelovetaco: Update für jsonb/ pg 9.4 hinzugefügt . Nebenbei: Für den einfachen Fall (1 Verschachtelungsebene) macht der ->Bediener auch den Trick für jsonin S. 9.3.
Erwin Brandstetter

1
@pacothelovetaco, für Seite 9.3 ist '#>' nicht die geheime Sauce, '->' wäre in Ordnung für Ihren Fall, da es auch das json-Objekt zurückgibt. '#>' wäre im Fall eines verschachtelten JSON-Pfads hilfreicher, da Sie den Pfad auf einfache Weise im '{}'
Gob00st

1
@> '[{"src": "foo.png"}]'; funktionieren gut unter welchen Bedingungen, aber wie löscht man ein bestimmtes Objekt wie dieses? Ich kenne den Index dieses Objekts nicht. Ich möchte nach Schlüsselwert löschen.
Pranay Soni

1
@PranaySoni: Bitte stellen Sie die neue Frage als Frage . Kommentare sind nicht der richtige Ort. Sie können jederzeit einen Link zu diesem für den Kontext erstellen.
Erwin Brandstetter

Lieber @ErwinBrandstetter, ist es möglich, beide Dokumente durch partiellen Abgleich zu finden? Zum Beispiel möchte ich beide Platten so etwas wie '[{"src": ". Png"}]'
Pyrejkee

7

Erstellen Sie eine Tabelle mit einer Spalte vom Typ json

CREATE TABLE friends ( id serial primary key, data jsonb);

Fügen wir nun json-Daten ein

INSERT INTO friends(data) VALUES ('{"name": "Arya", "work": ["Improvements", "Office"], "available": true}');
INSERT INTO friends(data) VALUES ('{"name": "Tim Cook", "work": ["Cook", "ceo", "Play"], "uses": ["baseball", "laptop"], "available": false}');

Lassen Sie uns nun einige Abfragen zum Abrufen von Daten durchführen

select data->'name' from friends;
select data->'name' as name, data->'work' as work from friends;

Möglicherweise haben Sie bemerkt, dass die Ergebnisse mit Anführungszeichen (") und Klammern ([]) versehen sind.

    name    |            work            
------------+----------------------------
 "Arya"     | ["Improvements", "Office"]
 "Tim Cook" | ["Cook", "ceo", "Play"]
(2 rows)

Um nur die Werte abzurufen, verwenden Sie einfach ->>

select data->>'name' as name, data->'work'->>0 as work from friends;
select data->>'name' as name, data->'work'->>0 as work from friends where data->>'name'='Arya';

22
Dies ist angenehm formatiertes Rauschen ohne erkennbaren Zusammenhang mit der Frage.
Erwin Brandstetter

4
Ich fand das nützlich. Zeigt, wie man in einem jsonb in das Array bohrt
GavinBelson

0

Wählen Sie Daten -> 'Objekte' -> 0 -> 'src' als SRC aus der Tabelle, in der Daten -> 'Objekte' -> 0 -> 'src' = 'foo.png'


2
Dies wäre NUR dann nützlich, wenn Sie den Index kennen, der 0 war.
Buyut Joko Rivai

Ja, aber es gibt eine Möglichkeit, ein Array-Objekt zu explodieren, das zeilenweise zugeordnet wird, und wir können das verwenden. Korrigieren Sie mich, wenn ich falsch liege.
Anand Shukla

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.