Если я использую array_agg
для сбора имен, я получаю имена через запятую, но в случае, если есть null
значение, это значение 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 ()?
Ответы:
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
источник
array_to_string(array_agg(...))
вы также можете использоватьstring_agg
.С 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;
источник
При решении общего вопроса об удалении нулей из агрегатов массивов есть два основных способа решения проблемы: либо выполнение array_agg (unnest (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 = '{}' );
Вызов второго (естественно) выглядит немного лучше, чем первый:
источник
Если вы ищете современный ответ на общий вопрос о том, как удалить NULL из массива , это:
Мне было особенно интересно узнать о производительности, и я хотел сравнить это с лучшей альтернативой:
CREATE OR REPLACE FUNCTION strip_nulls( IN array_in ANYARRAY ) RETURNS anyarray AS ' SELECT array_agg(a) FROM unnest(array_in) a WHERE a IS NOT NULL ; ' LANGUAGE sql ;
Тест pgbench доказал (с большой уверенностью), что array_remove () работает чуть более чем в два раза быстрее . Я провел свой тест на числах двойной точности с различными размерами массивов (10, 100 и 1000 элементов) и случайными значениями NULL между ними.
Также стоит отметить, что это можно использовать для удаления пробелов (''! = NULL). Но второй параметр принимает
anyelement
, и, поскольку, скорее всего, вы укажете пробел с помощью строкового литерала, обязательно приведите его к желаемой форме, обычно не к массиву.Например:
select array_remove(array['abc', ''], ''::text);
Если вы пытаетесь:
select array_remove(array['abc', ''], '');
он будет считать, что это TEXT [] (массив), и выдаст эту ошибку:
источник
Я добавляю это, хотя этот поток довольно старый, но я столкнулся с этим изящным трюком, который довольно хорошо работает с небольшими массивами. Он работает на Postgres 8.4+ без дополнительных библиотек или функций.
array_to_string()
Метод фактически избавляется от нулей.источник
Как было предложено в комментариях, вы можете написать функцию для замены нулей в массиве, однако, как также указано в потоке, связанном с комментариями, этот вид побеждает эффективность агрегатной функции, если вам нужно создать агрегат , разделите его и снова объедините.
Я думаю, что сохранение нулей в массиве - это просто (возможно, нежелательная) функция 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
источник
Это очень просто, просто сначала создайте новый оператор - (минус) для text [] :
CREATE OR REPLACE FUNCTION diff_elements_text ( text[], text[] ) RETURNS text[] as $$ SELECT array_agg(DISTINCT new_arr.elem) FROM unnest($1) as new_arr(elem) LEFT OUTER JOIN unnest($2) as old_arr(elem) ON new_arr.elem = old_arr.elem WHERE old_arr.elem IS NULL $$ LANGUAGE SQL IMMUTABLE; CREATE OPERATOR - ( PROCEDURE = diff_elements_text, leftarg = text[], rightarg = text[] );
И просто вычтите массив [null]:
select array_agg(x)-array[''] from ( select 'Y' x union all select null union all select 'N' union all select '' ) x;
Вот и все:
{Д, Н}
источник
array_agg(x) FILTER (WHERE x is not null)
кажется намного проще: dbfiddle.uk/… и вам действительно не нужна ваша собственная функция, вы можете просто использоватьarray_remove()
dbfiddle.uk/…Более важный вопрос - зачем использовать сразу все комбинации пользователей и групп. Гарантированно, что ваш пользовательский интерфейс не может обрабатывать все эти данные. Добавление подкачки к негабаритным данным также является плохой идеей. Попросите пользователей отфильтровать набор, прежде чем они увидят данные. Убедитесь, что ваш набор параметров JOIN находится в списке, чтобы они могли фильтровать производительность, если захотят. Иногда два запроса делают пользователей счастливее, если они оба работают быстро.
источник