¿cómo excluir valores nulos en array agg como en string agg usando postgres?


Si utilizo array_agg para recopilar nombres, obtengo mis nombres separados por comas, pero en caso de que haya un valor null, ese null también se toma como un nombre en el agregado. Por ejemplo :

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;

Devuelve ,Larry,Phil en lugar de solo Larry,Phil (en mi 9.1.2, muestra NULL,Larry,Phil). como en este violín

En cambio, si uso string_agg(), me muestra solo los nombres (sin comas vacías o nulos) como aquí

El problema es que tengo Postgres 8.4 instalado en el servidor, y string_agg() no funciona ahí. ¿Hay alguna manera de hacer que array_agg funcione de forma similar a string_agg() ?

Author: Daud, 2012-10-29

6 answers

SQL Fiddle

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

O, más simple y puede ser más barato, usando array_to_string que elimina los nulos:

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

SQL Fiddle

 16
Author: Clodoaldo Neto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-29 17:39:38

Con postgresql-9.3 uno puede hacer esto;

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;

Actualización : con 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;
 154
Author: Dale O'Brien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-12 01:14:03

Para resolver la cuestión general de eliminar los nulos de los agregados de matrices, hay dos formas principales de atacar el problema: o bien haciendo array_agg(unnest(array_agg(x)) o creando un agregado personalizado.

El primero es de la forma mostrada arriba :

SELECT 
    array_agg(u) 
FROM (
    SELECT 
        unnest(
            array_agg(v)
        ) as u 
    FROM 
        x
    ) un
WHERE 
    u IS NOT NULL;

El segundo:

/*
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 = '{}'
);

Llamar al segundo es (naturalmente) un poco más bonito que el primero:

Seleccione array_agg_notnull (v) desde x;

 10
Author: rorycl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:13

Estoy agregando esto a pesar de que este hilo es bastante viejo, pero me encontré con este truco limpio que funciona bastante bien en arreglos pequeños. Se ejecuta en Postgres 8.4 + sin bibliotecas o funciones adicionales.

string_to_array(array_to_string(array_agg(my_column)))::int[]

El método array_to_string() en realidad se deshace de los nulos.

 6
Author: ced-b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-25 00:41:14

Como se ha sugerido en los comentarios, puede escribir una función para reemplazar los nulos en una matriz, sin embargo, como también se señaló en el hilo vinculado en los comentarios, este tipo de derrota la eficiencia de la función aggregate si tiene que crear un agregado, dividirlo y agregarlo de nuevo.

Creo que mantener null en el array es solo una característica (quizás no deseada) de Array_Agg. Puedes usar subconsultas para evitar esto:

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

VIOLÍN SQL

 3
Author: GarethD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-29 14:54:56

Una pregunta más grande es por qué tirar de todos los combos de usuario/grupo a la vez. Garantizado su interfaz de usuario no puede manejar todos esos datos. Agregar paginación a datos sobredimensionados también es una mala idea. Haz que los usuarios filtren el conjunto antes de que vean los datos. Asegúrese de que su conjunto de opciones de unión esté en la lista para que puedan filtrar el rendimiento si lo desean. A veces 2 consultas hacen a los usuarios más felices si ambos son rápidos.

 -3
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-29 17:07:46