web-dev-qa-db-fra.com

Retourner la chaîne vide lorsque string_agg n'a pas d'enregistrement

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?

Exemple

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: ')?

5
Michael Underwood

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

Pourquoi 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, et array_agg Retourne null plutôt qu'un tableau vide lorsqu'il n'y a pas de lignes d'entrée. La fonction coalesce peut être utilisée pour substituer zéro ou un tableau vide pour NULL si nécessaire.

En rapport:

7
Erwin Brandstetter

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 autre option

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.

1
Michael Underwood