Comment obtenir un objet particulier du tableau jsonb dans PostgreSQL?

15

J'ai un champ appelé «utilisateur» qui contient un tableau json qui ressemble à peu près à ceci:

"user":

[{ "_id" : "1", "count" : "4" }, { "_id" : "3", "count": "4"}]

Maintenant, je veux une requête comme:

select count from tablename where id = "1"

Je ne suis pas en mesure d'obtenir le champ particulier à countpartir d'un tableau d'objets json dans PostgreSQL 9.4.

Rabi C Shah
la source

Réponses:

17

Il serait beaucoup plus efficace de stocker vos valeurs dans un schéma normalisé. Cela dit, vous pouvez également le faire fonctionner avec votre configuration actuelle.

Hypothèses

En supposant cette définition de table:

CREATE TABLE tbl (tbl_id int, usr jsonb);

"utilisateur" est un mot réservé et nécessiterait des guillemets doubles pour être utilisé comme nom de colonne. Ne fais pas ça. J'utilise à la usrplace.

Requete

La requête n'est pas aussi triviale que les commentaires (maintenant supprimés) semblaient:

SELECT t.tbl_id, obj.val->>'count' AS count
FROM   tbl t
JOIN   LATERAL jsonb_array_elements(t.usr) obj(val) ON obj.val->>'_id' = '1'
WHERE  t.usr @> '[{"_id":"1"}]';

Il y a 3 étapes de base :

1. Identifiez les lignes éligibles à moindre coût

WHERE t.usr @> '[{"_id":"1"}]'identifie les lignes avec un objet correspondant dans le tableau JSON. L'expression peut utiliser un index GIN générique sur la jsonbcolonne, ou un avec la classe d'opérateur plus spécialisée jsonb_path_ops:

CREATE INDEX tbl_usr_gin_idx ON tbl USING gin (usr jsonb_path_ops);

La WHEREclause ajoutée est logiquement redondante , mais elle est requise pour utiliser l'index. L'expression de la clause join applique la même condition, mais uniquement après avoir supprimé le tableau dans chaque ligne qualifiée jusqu'à présent. Avec la prise en charge des index, Postgres ne traite que les lignes contenant un objet éligible pour commencer. Peu importe avec de petites tables, fait une énorme différence avec de grandes tables et seulement quelques lignes qualificatives.

En relation:

2. Identifiez les objets correspondants dans le tableau

Unnest avec jsonb_array_elements(). ( unnest()n'est valable que pour les types de tableaux Postgres.) Comme nous ne sommes intéressés que par la mise en correspondance d'objets, filtrez immédiatement la condition de jointure.

En relation:

3. Extraire la valeur de la clé imbriquée 'count'

Après des objets qualifiés ont été extraites, tout simplement: obj.val->>'count'.

Erwin Brandstetter
la source
2
D'où obj(value)vient-il? Est-ce sur le LATERAL JOIN, le jsonb_array_elementsou ailleurs?
Tyler DeWitt
Il semble que la mise en forme ait pu être bousillée. Suis-je le lire correctement JOIN LATERAL jsonb_array_elements(t.usr) obj(value) is short for JOIN LATERAL jsonb_array_elements(t.usr) AS obj(value)et qui obj(value)est un alias de table et de colonne? Dans cet exemple, s'il objs'agit d'un alias de table, à quoi sert-il un alias? L'ensemble est revenu de jsonb_array_elements?
Tyler DeWitt
1
Oui et oui. j'ai supprimé mon commentaire brouillé.
Erwin Brandstetter
Est-il nécessaire d'utiliser l'alias de colonne? Dans mes tests, a JOIN LATERAL jsonb_array_elements(t.usr) obj ON obj->>'_id' = '1'eu le même effet (une fois que vous avez mis à jour l'instruction select à utiliser à la valueplace de val). Il semble que jsonb_array_elements(t.usr)renvoie un tableau avec une seule colonne. Les postgres sont-ils intelligents et réalisent-ils obj ->>la même chose que obj.val ->>?
Tyler DeWitt
Avec une seule colonne, Postgres utilise un alias donné comme nom de table et de colonne. Je suis juste explicite car il existe de nombreuses fonctions de retour d'ensemble qui retournent plus d'une colonne.
Erwin Brandstetter