Как исключить нулевые значения в array_agg, например, в string_agg, используя postgres?
Если я использую array_agg
для сбора имен, я получаю свои имена через запятую, но в случае, если есть значение null
, этот нуль также принимается за имя в агрегате. Например:
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;
он возвращает ,Larry,Phil
вместо Larry,Phil
(в моем 9.1.2 он показывает NULL,Larry,Phil
). как в этот скрипт
Вместо этого, если я использую string_agg()
, он показывает мне только имена (без пустых запятых или нулей), например здесь
Проблема в том, что на сервере установлена Postgres 8.4
, а string_agg()
там не работает. Есть ли способ заставить array_agg работать подобно string_agg()?
Ответы
Ответ 1
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
Или проще и может быть дешевле, используя array_to_string
, который устраняет нули:
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
Ответ 2
С помощью postgresql-9.3 можно сделать это:
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;
Обновить: с 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;
Ответ 3
При решении общего вопроса об удалении нулей из агрегатов массива существуют два основных способа атаки на проблему: либо делать array_agg (unsest (array_agg (x)), либо создавать настраиваемый агрегат.
Первая из приведенных выше форм выше:
SELECT
array_agg(u)
FROM (
SELECT
unnest(
array_agg(v)
) as u
FROM
x
) un
WHERE
u IS NOT NULL;
Второе:
/*
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 = '{}'
);
Вызов второго (естественно) немного приятнее, чем первый:
выберите array_agg_notnull (v) из x;
Ответ 4
Я добавляю это, хотя этот поток довольно старый, но я столкнулся с этим опрятным трюком, который хорошо работает на небольших массивах. Он работает на Postgres 8.4+ без дополнительных библиотек или функций.
string_to_array(array_to_string(array_agg(my_column)))::int[]
Метод array_to_string()
фактически избавляет от нулей.
Ответ 5
Как было предложено в комментариях, вы можете написать функцию для замены нулей в массиве, однако, как также указано в потоке, связанном с комментариями, этот вид побеждает эффективность агрегатной функции, если вам нужно создать агрегат, разбить его, а затем снова заполнить его.
Я думаю, что сохранение нулей в массиве - это просто (возможно, нежелательная) функция Array_Agg. Вы можете использовать подзапросы, чтобы избежать этого:
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
SQL FIDDLE
Ответ 6
Больший вопрос заключается в том, почему вы сразу же вытаскиваете все пользовательские/групповые комбо. Гарантируйте, что ваш пользовательский интерфейс не может обрабатывать все эти данные. Добавление пейджинга к негабаритным данным - также плохая идея. Попросите своих пользователей фильтровать набор до того, как он увидит данные. Убедитесь, что установлен параметр JOIN в списке, чтобы они могли фильтровать производительность, если захотят. Иногда 2 запроса делают пользователей более счастливыми, если они оба быстры.