PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Postgres 9.4 matrice jsonb come tabella

Interrogazione

Manca la definizione della tua tabella. Supponendo:

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

Per trovare il value e la sua riga per un dato oid e 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'

Questo è un implicito LATERAL giuntura. Confronta:

  • Query per gli elementi dell'array all'interno del tipo JSON

2) Qual è il modo più veloce per ottenere una tabella con 3 colonne di oid , instance e value.

Suppongo di usare jsonb_populate_recordset() , quindi puoi fornire i tipi di dati nella definizione della tabella. Assumendo text per tutti:

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

Potrebbe anche essere una tabella persistente (non temporanea). Questo è solo per la sessione corrente. Quindi:

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

È tutto. La prima query riscritta:

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

Ma è più lento rispetto alla prima domanda. La chiave per le prestazioni con tabelle più grandi è il supporto dell'indice:

Indice

Potresti facilmente indicizzare la tabella normalizzata (tradotta) o il layout alternativo che hai proposto nella domanda. Indicizzazione del tuo layout attuale non è così ovvio, ma anche possibile. Per le migliori prestazioni suggerisco un indice funzionale solo sui data chiave con il jsonb_path_ops classe operatore. Per documentazione:

La differenza tecnica tra un jsonb_ops e un jsonb_path_ops GINindex è che il primo crea elementi di indice indipendenti per ogni chiave e valore nei dati, mentre il secondo crea elementi di indice solo per ogni valore nei dati.

Questo dovrebbe fare miracoli per le prestazioni:

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

Ci si potrebbe aspettare che funzioni solo una corrispondenza completa per un elemento dell'array JSON, come:

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

Nota la notazione dell'array JSON (con che racchiude [] ) del valore fornito, che è obbligatorio.

Ma elementi dell'array con un sottoinsieme di chiavi funziona anche tu:

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

La parte difficile è incorporare il tuo predicato aggiunto apparentemente insospettabile value <> '1' . È necessario prestare attenzione nell'applicare tutti i predicati allo stesso elemento dell'array. Potresti combinare questo con la prima query:

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à.

Indice speciale

Se la tua tabella è enorme, la dimensione dell'indice potrebbe essere un fattore decisivo. Puoi confrontare le prestazioni di questa soluzione speciale con un indice funzionale:

Questa funzione estrae un array Postgres di id-instance combinazioni da un dato jsonb valore:

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$

Possiamo costruire un indice funzionale basato su questo:

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

E basa la query su di esso:

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

L'idea è che l'indice dovrebbe essere sostanzialmente più piccolo perché memorizza solo i valori combinati senza chiavi. La matrice operatore di contenimento @> stesso dovrebbe funzionare in modo simile all'operatore di contenimento jsonb @> . Non mi aspetto una grande differenza, ma mi interesserebbe molto che sia più veloce.

Simile alla prima soluzione in questa risposta correlata (ma più specializzata):

  • Indice per trovare un elemento in un array JSON

A parte:

  • Non userei oid come nome di colonna poiché viene utilizzato anche per scopi interni in Postgres.
  • Se possibile, userei una tabella normale e normalizzata senza JSON.