Requête JSONB dans PostgreSQL


13

J'ai une table, personsqui contient deux colonnes, une idet une datacolonne basée sur JSONB (cette table vient d'être créée à des fins de démonstration pour jouer avec le support JSON de PostgreSQL).

Maintenant, supposons qu'il contienne deux enregistrements:

1, { name: 'John', age: 30 }
2, { name: 'Jane', age: 20 }

Maintenant, je suppose que je veux obtenir le nom de toute personne âgée de plus de 25 ans. Ce que j'ai essayé, c'est:

select data->'name' as name from persons where data->'age' > 25

Malheureusement, cela entraîne une erreur. Je peux le résoudre en utilisant ->>au lieu de ->, mais les comparaisons ne fonctionnent plus comme prévu, car non les chiffres sont comparés, mais leurs représentations sous forme de chaînes:

select data->'name' as name from persons where data->>'age' > '25'

J'ai ensuite compris que je pouvais réellement résoudre le problème en utilisant ->et un cast pour int:

select data->'name' as name from persons where cast(data->'age' as int) > 25

Cela fonctionne, mais ce n'est pas si agréable que je dois connaître le type réel (le type de agedans le document JSON estnumber toute façon, alors pourquoi PostgreSQL ne peut-il pas le comprendre par lui-même?).

J'ai ensuite compris que si je convertis manuellement en textutilisant la ::syntaxe, tout fonctionne aussi comme prévu - bien que nous comparions à nouveau les chaînes.

select data->'name' as name from persons where data->'age'::text > '25'

Si j'essaye ensuite avec le nom au lieu de l'âge, cela ne fonctionne pas:

select data->'name' as name from persons where data->'name'::text > 'Jenny'

Il en résulte une erreur:

syntaxe d'entrée non valide pour le type json

De toute évidence, je ne reçois pas quelque chose ici. Malheureusement, il est assez difficile de trouver des exemples concrets d'utilisation de JSON avec PostgreSQL.

Des indices?


1
Dans data->'name'::text, vous transformez la 'name'chaîne en texte, pas le résultat. Vous n'obtenez pas d'erreur lors de la comparaison avec '25'car 25est un littéral JSON valide; mais Jennyn'est pas (bien que ce "Jenny"serait).
chirlu

Merci, c'est la solution :-). J'ai confondu 'Jenny'avec '"Jenny"'.
Golo Roden

Réponses:


14

Cela ne fonctionne pas car il essaie de convertir une jsonbvaleur en integer.

select data->'name' as name from persons where cast(data->'age' as int) > 25

Cela fonctionnerait en fait:

SELECT data->'name' AS name FROM persons WHERE cast(data->>'age' AS int) > 25;

Ou plus court:

SELECT data->'name' AS name FROM persons WHERE (data->>'age')::int > 25;

Et ça:

SELECT data->'name' AS name FROM persons WHERE data->>'name' > 'Jenny';

Semble comme une confusion avec les deux opérateurs ->et->> et la priorité des opérateurs . Le casting:: plus fort que les opérateurs json (b).

Comprendre le type dynamiquement

Voici la partie la plus intéressante de votre question:

le type d'âge dans le document JSON est de toute façon numéro, alors pourquoi PostgreSQL ne peut-il pas le comprendre par lui-même?

SQL est un langage strictement typé, il ne permet pas à la même expression d'évaluer integerdans une ligne et textdans la suivante. Mais comme vous n'êtes intéressé que par le booleanrésultat du test, vous pouvez contourner cette restriction avec unCASE expression qui bifurque en fonction du résultat de jsonb_typeof():

SELECT data->'name'
FROM   persons
WHERE  CASE jsonb_typeof(data->'age')
        WHEN 'number'  THEN (data->>'age')::numeric > '25' -- treated as numeric
        WHEN 'string'  THEN data->>'age' > 'age_level_3'   -- treated as text
        WHEN 'boolean' THEN (data->>'age')::bool           -- use boolean directly (example)
        ELSE FALSE                                         -- remaining: array, object, null
       END;

Une chaîne de caractères non typée à droite de la > opérateur est contraint automatiquement au type respectif de la valeur à gauche. Si vous y mettez une valeur typée, le type doit correspondre ou vous devez le caster explicitement - à moins qu'il n'y ait un cast implicite adéquat enregistré dans le système.

Si vous savez que toutes les valeurs numériques sont réellement integer, vous pouvez également:

... (data->>'age')::int > 25 ...

quelle est l'expression de base sqlalchemy pour la comparaison ci-dessus de l'instruction select, par exemple. s = select ([issues]). where (issues.c.id == mid) .select_from (issues, ..... externaljoin (issues.c.data ['type_id'] == mtypes.c.id) ) ... Voici issues.c.data type de données jsonb et est comparé avec mtypes.c.id de type entier
user956424
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.