J'ai une table, persons
, qui contient deux colonnes, une id
et une colonne data
basée sur JSONB (cette table vient d'être créée à des fins de démonstration pour jouer avec Prise en charge 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 pas les nombres 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 en 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 doive connaître le type réel (le type de age
dans le document JSON est number
de toute façon, alors pourquoi PostgreSQL ne peut-il pas le comprendre par lui-même ?).
J'ai ensuite compris que si je convertissais manuellement en text
en utilisant le ::
syntaxe, tout fonctionne comme prévu, même si nous comparons à 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 n'ai rien ici. Malheureusement, il est assez difficile de trouver des exemples concrets d'utilisation de JSON avec PostgreSQL.
Des indices?
Cela ne fonctionne pas car il essaie de convertir une valeur jsonb
en integer
.
select data->'name' as name from persons where cast(data->'age' as int) > 25
This 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 priorité de l'opérateur . Le cast ::
Se lie plus fort que les opérateurs json (b).
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 integer
sur une ligne et text
sur la suivante. Mais comme vous n'êtes intéressé que par le résultat boolean
du test, vous pouvez contourner cette restriction avec une expression CASE
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;
Un littéral de chaîne non typé à droite de l'opérateur >
Est automatiquement contraint au type respectif de la valeur à gauche. Si vous y mettez une valeur typée, le type doit correspondre ou vous devez le cast 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 en fait integer
, vous pouvez également:
... (data->>'age')::int > 25 ...