PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Tableau jsonb Postgres 9.4 sous forme de table

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 et value.

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 un jsonb_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.