Le type de données de la colonne people
est json
, comme le résultat de json_array_elements(people)
. Et il n'y a pas d'opérateur d'égalité ( =
) pour le type de données json
. Vous ne pouvez donc pas non plus courir GROUP BY
dessus. Plus:
jsonb
a un opérateur d'égalité, donc la "solution de contournement" dans votre réponse consiste à convertir jsonb
et à utiliser l'équivalent jsonb_array_elements()
. Le casting ajoute du coût:
jsonb_array_elements(people::jsonb)
Depuis Postgres 9.4, nous avons également json_array_elements_text(json)
renvoyé des éléments de tableau sous la forme text
. En relation:
Donc:
SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
GROUP BY p.name;
Il semble plus pratique d'obtenir des noms text
plutôt que des jsonb
objets (entre guillemets dans la représentation textuelle) et votre "sortie souhaitée" indique que vous voulez / avez besoin text
dans le résultat pour commencer.
GROUP BY
sur les text
données est également moins cher que sur jsonb
, donc cette solution alternative devrait être plus rapide pour deux raisons. (Testez avec EXPLAIN (ANALYZE, TIMING OFF)
.)
Pour mémoire, il n'y avait rien de mal avec votre réponse originale . La virgule ( ,
) est tout aussi "correcte" que CROSS JOIN LATERAL
. Le fait d'avoir été défini plus tôt dans SQL standard ne le rend pas inférieur. Voir:
Il n'est pas plus portable à d' autres SGBDR, et depuis jsonb_array_elements()
ou json_array_elements_text()
ne sont pas portables à d' autres SGBDR pour commencer, qui est également hors de propos. La courte requête ne devient pas plus claire avec CROSS JOIN LATERAL
IMO, mais le dernier bit est juste mon opinion personnelle.
J'ai utilisé l'alias de table et de colonne plus explicite p(name)
et la référence qualifiée de table p.name
pour se défendre contre les noms en double possibles. name
est un mot si courant qu'il peut également apparaître comme nom de colonne dans la table sous-jacente band
, auquel cas il se résoudrait en silence band.name
. Le formulaire simple json_array_elements_text(people) name
attache uniquement un alias de table , le nom de la colonne est toujours value
, tel que renvoyé par la fonction. Mais name
résout sa seule colonne value
lorsqu'il est utilisé dans la SELECT
liste. Cela fonctionne comme prévu . Mais un vrai nom de colonne name
(s'il band.name
devrait exister) se lierait en premier. Bien que cela ne morde pas dans l'exemple donné, il peut s'agir d'une arme à pied chargée dans d'autres cas.
N'utilisez pas le "nom" générique comme identifiant pour commencer. Peut-être que c'était juste pour le cas de test simple.
Si la colonne people
peut contenir autre chose qu'un simple tableau JSON , l'une ou l'autre requête déclencherait une exception. Si vous ne pouvez pas garantir l'intégrité des données, vous voudrez peut-être vous défendre avec json_typeof()
:
SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
WHERE json_typeof(b.people) = 'array'
GROUP BY 1; -- optional short syntax since you seem to prefer short syntax
Exclut les lignes violées de la requête.
En relation: