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.