PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Postgres 9.4 jsonb-Array als Tabelle

Abfrage

Ihre Tabellendefinition fehlt. Angenommen:

CREATE TABLE configuration (
  config_id serial PRIMARY KEY
, config jsonb NOT NULL
);

Um den value zu finden und seine Zeile für gegebenen oid und instance :

SELECT c.config_id, d->>'value' AS value
FROM   configuration c
     , jsonb_array_elements(config->'data') d  -- default col name is "value"
WHERE  d->>'oid'      = '1.3.6.1.4.1.7352.3.10.2.5.35.3'
AND    d->>'instance' = '0'
AND    d->>'value'   <> '1'

Das ist ein implizites LATERAL beitreten. Vergleichen Sie:

  • Suche nach Array-Elementen innerhalb des JSON-Typs

2) Was ist der schnellste Weg, um eine Tabelle mit 3 Spalten von oid zu erhalten , instance und value.

Ich nehme an, jsonb_populate_recordset() zu verwenden , dann können Sie Datentypen in der Tabellendefinition bereitstellen. Angenommen text für alle:

CREATE TEMP TABLE data_pattern (oid text, value text, instance text);

Könnte auch eine dauerhafte (nicht temporäre) Tabelle sein. Dieser gilt nur für die aktuelle Sitzung. Dann:

SELECT c.config_id, d.*
FROM   configuration c
     , jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d

Das ist alles. Die erste umgeschriebene Abfrage:

SELECT c.config_id, d.*
FROM   configuration c
     , jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d
WHERE  d.oid      = '1.3.6.1.4.1.7352.3.10.2.5.35.3'
AND    d.instance = '0'
AND    d.value   <> '1';

Aber das ist langsamer als die erste Abfrage. Der Schlüssel zur Leistung bei größeren Tabellen ist die Indexunterstützung:

Index

Sie könnten die normalisierte (übersetzte) Tabelle oder das alternative Layout, das Sie in der Frage vorgeschlagen haben, leicht indizieren. Indizierung Ihres aktuellen Layouts ist nicht so offensichtlich, aber auch möglich. Für die beste Leistung schlage ich einen funktionalen Index nur für die data vor Schlüssel mit dem jsonb_path_ops Betreiberklasse. Per Dokumentation:

Der technische Unterschied zwischen einem jsonb_ops und ein jsonb_path_ops GINindex besteht darin, dass Ersteres unabhängige Indexelemente für jeden Schlüssel und Wert in den Daten erstellt, während Letzteres Indexelemente nur für jeden Wert in den Daten erstellt.

Das sollte Wunder wirken für Leistung:

CREATE INDEX configuration_my_idx ON configuration
USING gin ((config->'data') jsonb_path_ops);

Man könnte erwarten, dass nur eine vollständige Übereinstimmung für ein JSON-Array-Element funktionieren würde, wie zum Beispiel:

SELECT * FROM configuration
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
                            , "instance": "0", "value": "1234"}]';

Beachten Sie die JSON-Array-Notation (mit umschließendem [] ) des bereitgestellten Werts, das ist erforderlich.

Aber Array-Elemente mit einer Teilmenge von Schlüsseln funktioniert auch:

SELECT * FROM configuration
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
                            , "instance": "0"}]'

Der schwierige Teil besteht darin, Ihr scheinbar unverdächtiges hinzugefügtes Prädikat value <> '1' zu integrieren . Es muss darauf geachtet werden, alle Prädikate gleich anzuwenden Array-Element. Sie könnten dies mit der ersten Abfrage kombinieren:

SELECT c.*, d->>'value' AS value
FROM   configuration c
     , jsonb_array_elements(config->'data') d
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3", "instance": "0"}]'
AND    d->>'oid'      = '1.3.6.1.4.1.7352.3.10.2.5.35.3'  -- must be repeated
AND    d->>'instance' = '0'                               -- must be repeated
AND    d->>'value'   <> '1'                               -- here we can rule out

Voilá.

Spezialindex

Wenn Ihre Tabelle sehr groß ist, kann die Indexgröße ein entscheidender Faktor sein. Sie können die Leistung dieser speziellen Lösung mit einem funktionalen Index vergleichen:

Diese Funktion extrahiert ein Postgres-Array von oid-instance Kombinationen aus einem bestimmten jsonb Wert:

CREATE OR REPLACE FUNCTION f_config_json2arr(_j jsonb)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
$func$
SELECT ARRAY(
   SELECT (elem->>'oid') || '-' || (elem->>'instance')
   FROM   jsonb_array_elements(_j) elem
   )
$func$

Darauf aufbauend können wir einen funktionalen Index erstellen:

CREATE INDEX configuration_conrfig_special_idx ON configuration
USING  gin (f_config_json2arr(config->'data'));

Und die Abfrage darauf aufbauen:

SELECT * FROM configuration
WHERE  f_config_json2arr(config->'data') @> '{1.3.6.1.4.1.7352.3.10.2.5.35.3-0}'::text[]

Die Idee ist, dass der Index wesentlich kleiner sein sollte, da er nur die kombinierten Werte ohne Schlüssel speichert. Das Array Containment-Operator @> selbst sollte ähnlich wie der jsonb Containment-Operator @> funktionieren . Ich erwarte keinen großen Unterschied, aber mich würde sehr interessieren, was schneller ist.

Ähnlich wie die erste Lösung in dieser verwandten Antwort (aber spezialisierter):

  • Index zum Auffinden eines Elements in einem JSON-Array

Nebenbei:

  • Ich würde oid nicht verwenden als Spaltenname, da dieser auch für interne Zwecke in Postgres verwendet wird.
  • Wenn möglich, würde ich eine einfache, normalisierte Tabelle ohne JSON verwenden.