J'essaie de renvoyer un champ de texte dans une requête PostgreSQL qui est du formulaire
'stringOne' || string_agg(field, ',') || 'stringTwo'
où certains éléments de la clause du groupe, field
est toujours null. Je veux, et attendez-vous, de finir avec stringOnestringTwo
dans ce cas, mais au lieu de cela, je reçois NULL
.
Pourquoi est-ce et comment puis-je accomplir ce que j'essaie de faire?
Supposons que j'ai les tables
foo bar
+----+--------+ +----+-------+--------------+
| id | name | | id | fooid | baz |
+----+--------+ +----+-------+--------------+
| 1 | FooOne | | 1 | 1 | FooOneBazOne |
| 2 | FooTwo | | 2 | 1 | FooTwoBazTwo |
+----+--------+ +----+-------+--------------+
et je cours la requête
SELECT
foo.name AS foo,
'Bazzes: ' || string_agg(bar.baz, ', ') AS bazzes
FROM
foo LEFT JOIN bar ON bar.fooid = foo.id
GROUP BY
foo.name
Ensuite, je veux (et attendez-vous) d'obtenir les Resulatset
+--------+------------------------------------+
| foo | bazzes |
+--------+------------------------------------+
| FooOne | Bazzes: FooOneBazOne, FooOneBazTwo |
| FooTwo | Bazzes: | <== NOT NULL
+--------+------------------------------------+
mais au lieu de cela, la deuxième ligne est ('FooTwo', NULL)
. Comment puis-je modifier cette requête afin que la deuxième ligne renvoie ('FooTwo', 'Bazzes: ')
?
Utilisez (COALESCE
Pour attraper et remplacer NULL
valeurs:
SELECT f.name AS foo
, 'Bazzes: ' || COALESCE(string_agg(b.baz, ', '), '') AS bazzes
FROM foo f
LEFT JOIN bar b ON b.fooid = f.id
GROUP BY 1;
concat()
est une autre option pratique que vous vous trouvez, en particulier pour concaténer multiple valeurs. Je suggère la variante ((concat_ws()
("avec séparateur"), cependant, pour éviter l'espace de fuite .
concat_ws(' ', 'Bazzes:', string_agg(b.baz, ', ')) AS bazzes
NULL
?Presque toutes les fonctions d'agrégat retour NULL
Si tous les champs source sont NULL
(aucune valeur non nulle, à préciser) - count()
étant l'exception pour des raisons pratiques. le manuel :
Il convient de noter que, à l'exception de
count
, ces fonctions renvoient une valeur null lorsque aucune ligne de lignes n'est sélectionnée. En particulier,sum
d'aucune ligne ne renvoie NULL, pas zéro comme on pouvait s'attendre, etarray_agg
Retourne null plutôt qu'un tableau vide lorsqu'il n'y a pas de lignes d'entrée. La fonctioncoalesce
peut être utilisée pour substituer zéro ou un tableau vide pour NULL si nécessaire.
En rapport:
La méthode la plus simple que j'ai constatée pour accomplir ceci est d'échanger l'opérateur de concaténation à chaîne pour la fonction de concaténation à chaîne concat()
. Pour une raison quelconque apparemment, le premier provoque l'ensemble du résultat de NULL
si un opérande est null, par opposition à ceux qui jette efficacement les arguments NULL
à ''
.
Donc, cette requête fonctionne comme si vous le souhaitez:
SELECT
foo.name,
concat('Bazzes: ', string_agg(bar.baz, ', ')) AS bazzes
FROM
foo LEFT JOIN bar ON bar.fooid = foo.id
GROUP BY
foo.name
Il renvoie le jeu de résultats souhaité:
+--------+------------------------------------+
| foo | bazzes |
+--------+------------------------------------+
| FooOne | Bazzes: FooOneBazOne, FooOneBazTwo |
| FooTwo | Bazzes: |
+--------+------------------------------------+
Une deuxième approche est légèrement plus impliquée, mais je l'ai trouvée en premier et cela peut être pertinent dans d'autres situations, je vais donc le documenter ici aussi. Une instruction CASE
peut être utilisée pour compter le nombre d'éléments avant de les transmettre à la fonction d'agrégation et renvoyer une chaîne vide au lieu de NULL
lorsque la réponse est nulle.
Dans la requête originale, remplacez
string_agg(bar.baz, ', ')
avec
CASE count(bar.baz) WHEN 0 THEN '' ELSE string_agg(bar.baz, ', ') END
et la fonction string_agg
ne sera appelée que lorsque l'au moins une chaîne est agrégée. La chaîne vide sera renvoyée et combinée correctement avec le reste du texte autrement, au lieu de contraindre le résultat complet à NULL
.