Requête
Votre définition de table est manquante. En supposant :
CREATE TABLE configuration (
config_id serial PRIMARY KEY
, config jsonb NOT NULL
);
Pour trouver une value
et sa ligne pour oid
donné et 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'
C'est un LATERAL
implicite rejoindre. Comparez :
- Requête pour les éléments de tableau dans le type JSON
2) Quel est le moyen le plus rapide d'obtenir un tableau avec 3 colonnes de
oid
,instance
etvalue.
Je suppose d'utiliser jsonb_populate_recordset()
, vous pouvez alors fournir des types de données dans la définition de table. En supposant text
pour tous :
CREATE TEMP TABLE data_pattern (oid text, value text, instance text);
Il peut également s'agir d'une table persistante (non temporaire). Celui-ci est uniquement pour la session en cours. Ensuite :
SELECT c.config_id, d.*
FROM configuration c
, jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d
C'est tout. La première requête réécrit :
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';
Mais c'est plus lent que la première requête. La clé des performances avec une table plus grande est la prise en charge des index :
Index
Vous pouvez facilement indexer le tableau normalisé (traduit) ou la mise en page alternative que vous avez proposée dans la question. Indexation de votre mise en page actuelle n'est pas aussi évident, mais aussi possible. Pour de meilleures performances, je suggère un index fonctionnel uniquement sur les data
clé avec le jsonb_path_ops
classe d'opérateur. Par documentation :
La différence technique entre un
jsonb_ops
et unjsonb_path_ops
GINindex est que le premier crée des éléments d'index indépendants pour chaque clé et valeur dans les données, tandis que le second crée des éléments d'index uniquement pour chaque valeur dans les données.
Cela devrait faire des merveilles pour les performances :
CREATE INDEX configuration_my_idx ON configuration
USING gin ((config->'data') jsonb_path_ops);
On pourrait s'attendre à ce que seule une correspondance complète pour un élément de tableau JSON fonctionne, comme :
SELECT * FROM configuration
WHERE (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
, "instance": "0", "value": "1234"}]';
Notez la notation de tableau JSON (avec englobant []
) de la valeur fournie, c'est obligatoire.
Mais les éléments de tableau avec un sous-ensemble de clés fonctionne aussi :
SELECT * FROM configuration
WHERE (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
, "instance": "0"}]'
Le plus difficile est d'incorporer votre prédicat ajouté apparemment non suspect value <> '1'
. Il faut veiller à appliquer tous les prédicats au même élément de tableau. Vous pouvez combiner ceci avec la première requête :
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à.
Index spécial
Si votre table est énorme, la taille de l'index peut être un facteur décisif. Vous pouvez comparer les performances de cette solution spéciale avec un indice fonctionnel :
Cette fonction extrait un tableau Postgres de oid-instance combinaisons à partir d'un jsonb
donné valeur :
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$
Nous pouvons construire un index fonctionnel basé sur ceci :
CREATE INDEX configuration_conrfig_special_idx ON configuration
USING gin (f_config_json2arr(config->'data'));
Et basez la requête dessus :
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'idée est que l'index doit être considérablement plus petit car il ne stocke que les valeurs combinées sans clé. Le tableau opérateur de confinement @>
lui-même devrait fonctionner de la même manière que l'opérateur de confinement jsonb @>
. Je ne m'attends pas à une grande différence, mais je serais très intéressé par ce qui est plus rapide.
Similaire à la première solution de cette réponse connexe (mais plus spécialisée) :
- Index pour trouver un élément dans un tableau JSON
À part :
- Je n'utiliserais pas
oid
comme nom de colonne puisqu'il est également utilisé à des fins internes dans Postgres. - Si possible, j'utiliserais une table simple et normalisée sans JSON.