Si j'utilise array_agg
Pour collecter les noms, je reçois mes noms séparés par des virgules, mais dans le cas où il existe une valeur null
, ce null est également pris comme nom dans l'agrégat. Par exemple :
SELECT g.id,
array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users,
array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users
FROM groups g
GROUP BY g.id;
il renvoie ,Larry,Phil
au lieu de simplement Larry,Phil
(dans mon 9.1.2, il affiche NULL,Larry,Phil
). comme dans this violon
Au lieu de cela, si j'utilise string_agg()
, il ne me montre que les noms (sans virgules vides ni null) comme ici
Le problème est que j'ai Postgres 8.4
Installé sur le serveur et que string_agg()
ne fonctionne pas là-bas. Existe-t-il un moyen de faire fonctionner array_agg similaire à string_agg ()?
select
id,
(select array_agg(a) from unnest(canonical_users) a where a is not null) canonical_users,
(select array_agg(a) from unnest(non_canonical_users) a where a is not null) non_canonical_users
from (
SELECT g.id,
array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users,
array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users
FROM groups g
GROUP BY g.id
) s
Ou, plus simple et peut être moins cher, en utilisant array_to_string
qui élimine les null:
SELECT
g.id,
array_to_string(
array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END)
, ','
) canonical_users,
array_to_string(
array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END)
, ','
) non_canonical_users
FROM groups g
GROUP BY g.id
Avec postgresql-9.3, on peut le faire;
SELECT g.id,
array_remove(array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END), NULL) canonical_users,
array_remove(array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END), NULL) non_canonical_users
FROM groups g
GROUP BY g.id;
Mise à jour: avec postgresql-9.4;
SELECT g.id,
array_agg(g.users) FILTER (WHERE g.canonical = 'Y') canonical_users,
array_agg(g.users) FILTER (WHERE g.canonical = 'N') non_canonical_users
FROM groups g
GROUP BY g.id;
Pour résoudre la question générale de la suppression des valeurs NULL des agrégats de tableaux, il existe deux façons principales d'attaquer le problème: soit en faisant array_agg (unnest (array_agg (x)), soit en créant un agrégat personnalisé.
Le premier est de la forme indiquée ci-dessus :
SELECT
array_agg(u)
FROM (
SELECT
unnest(
array_agg(v)
) as u
FROM
x
) un
WHERE
u IS NOT NULL;
La deuxième:
/*
With reference to
http://ejrh.wordpress.com/2011/09/27/denormalisation-aggregate-function-for-postgresql/
*/
CREATE OR REPLACE FUNCTION fn_array_agg_notnull (
a anyarray
, b anyelement
) RETURNS ANYARRAY
AS $$
BEGIN
IF b IS NOT NULL THEN
a := array_append(a, b);
END IF;
RETURN a;
END;
$$ IMMUTABLE LANGUAGE 'plpgsql';
CREATE AGGREGATE array_agg_notnull(ANYELEMENT) (
SFUNC = fn_array_agg_notnull,
STYPE = ANYARRAY,
INITCOND = '{}'
);
Appeler le second est (naturellement) un peu plus joli que le premier:
sélectionnez array_agg_notnull (v) parmi x;
J'ajoute cela même si ce fil est assez ancien, mais j'ai rencontré cette astuce intéressante qui fonctionne assez bien sur les petits tableaux. Il fonctionne sur Postgres 8.4+ sans bibliothèques ni fonctions supplémentaires.
string_to_array(array_to_string(array_agg(my_column)))::int[]
La méthode array_to_string()
supprime en fait les valeurs nulles.
Comme cela a été suggéré dans les commentaires, vous pouvez écrire une fonction pour remplacer les valeurs nulles dans un tableau, mais comme cela a également été souligné dans le thread lié à dans les commentaires, ce type de résultat nuit à l'efficacité de la fonction d'agrégation si vous devez créer un agrégat , le diviser puis l'agréger à nouveau.
Je pense que garder les valeurs nulles dans le tableau est juste une caractéristique (peut-être indésirable) d'Array_Agg. Vous pouvez utiliser des sous-requêtes pour éviter cela:
SELECT COALESCE(y.ID, n.ID) ID,
y.Users,
n.Users
FROM ( SELECT g.ID, ARRAY_AGG(g.Users) AS Users
FROM Groups g
WHERE g.Canonical = 'Y'
GROUP BY g.ID
) y
FULL JOIN
( SELECT g.ID, ARRAY_AGG(g.Users) AS Users
FROM Groups g
WHERE g.Canonical = 'N'
GROUP BY g.ID
) n
ON n.ID = y.ID