J'essaie de tester le type json
dans PostgreSQL 9.3.
J'ai une colonne json
appelée data
dans une table appelée reports
. Le JSON ressemble à ceci:
{
"objects": [
{"src":"foo.png"},
{"src":"bar.png"}
],
"background":"background.png"
}
J'aimerais interroger la table pour tous les rapports correspondant à la valeur "src" dans le tableau "objets". Par exemple, est-il possible d'interroger la base de données pour tous les rapports correspondant à 'src' = 'foo.png'
? J'ai écrit avec succès une requête pouvant correspondre au "background"
:
SELECT data AS data FROM reports where data->>'background' = 'background.png'
Mais comme "objects"
a un tableau de valeurs, je n'arrive pas à écrire quelque chose qui fonctionne. Est-il possible d'interroger la base de données pour tous les rapports correspondant à 'src' = 'foo.png'
? J'ai parcouru ces sources mais je ne peux toujours pas l'obtenir:
J'ai aussi essayé des choses comme ça mais en vain:
SELECT json_array_elements(data->'objects') AS data from reports
WHERE data->>'src' = 'foo.png';
Je ne suis pas un expert en SQL, donc je ne sais pas ce que je fais mal.
json
dans Postgres 9.3+Annulez l'activation du tableau JSON avec la fonction json_array_elements()
dans une jointure latérale dans la clause FROM
et testez-en les éléments:
WITH reports(data) AS (
VALUES ('{"objects":[{"src":"foo.png"}, {"src":"bar.png"}]
, "background":"background.png"}'::json)
)
SELECT *
FROM reports r, json_array_elements(r.data#>'{objects}') obj
WHERE obj->>'src' = 'foo.png';
La requête CTE (requête WITH
) ne fait que remplacer une table reports
.
Ou équivalent pour seulement un célibataire niveau d'imbrication:
SELECT *
FROM reports r, json_array_elements(r.data->'objects') obj
WHERE obj->>'src' = 'foo.png';
Les opérateurs ->>
, ->
et #>
sont expliqués dans le manuel.
Les deux requêtes utilisent un JOIN LATERAL
.
Réponse étroitement liée:
jsonb
dans Postgres 9.4+Utilisez l'équivalent jsonb_array_elements()
.
Mieux encore, utilisez le nouvel opérateur "contient" @>
(idéal en combinaison avec un index GIN correspondant sur l'expression data->'objects'
):
CREATE INDEX reports_data_gin_idx ON reports
USING gin ((data->'objects') jsonb_path_ops);
SELECT * FROM reports WHERE data->'objects' @> '[{"src":"foo.png"}]';
Puisque la clé objects
contient un JSON tableau, nous devons faire correspondre la structure dans le terme de recherche et placer l'élément de tableau entre crochets. Supprimez les crochets du tableau lors de la recherche d'un enregistrement brut.
Explication détaillée et plus d'options:
Créer une table avec une colonne de type json
# CREATE TABLE friends ( id serial primary key, data jsonb);
Maintenant, insérons les données JSON
# INSERT INTO friends(data) VALUES ('{"name": "Arya", "work": ["Improvements", "Office"], "available": true}');
# INSERT INTO friends(data) VALUES ('{"name": "Tim Cook", "work": ["Cook", "ceo", "Play"], "uses": ["baseball", "laptop"], "available": false}');
Faisons maintenant quelques requêtes pour récupérer des données
# select data->'name' from friends;
# select data->'name' as name, data->'work' as work from friends;
Vous avez peut-être remarqué que les résultats sont accompagnés de virgules inversées (") et de crochets ([])
name | work
------------+----------------------------
"Arya" | ["Improvements", "Office"]
"Tim Cook" | ["Cook", "ceo", "Play"]
(2 rows)
Maintenant, pour récupérer uniquement les valeurs, utilisez simplement ->>
# select data->>'name' as name, data->'work'->>0 as work from friends;
#select data->>'name' as name, data->'work'->>0 as work from friends where data->>'name'='Arya';