Как объединить строки строкового поля в запросе PostgreSQL 'group by'?
Я ищу способ объединить строки поля внутри группы по запросу. Так, например, у меня есть таблица:
ID COMPANY_ID EMPLOYEE
1 1 Anna
2 1 Bill
3 2 Carol
4 2 Dave
и я хотел сгруппироваться по company_id, чтобы получить что-то вроде:
COMPANY_ID EMPLOYEE
1 Anna, Bill
2 Carol, Dave
есть встроенная функция в MySQL, чтобы сделать это group_concat
11 ответов:
в PostgreSQL 9.0 или более поздней версии:
последние версии Postgres (с конца 2010 года) имеют
string_agg(expression, delimiter)
функция, которая будет делать именно то, что вопрос задан, даже позволяя указать строку разделителя:SELECT company_id, string_agg(employee, ', ') FROM mytable GROUP BY company_id;
и Postgres 9.0 также добавлена возможность указать
ORDER BY
п. в любом агрегатном выражение; в противном случае порядок не определен. Так что теперь вы можете написать:SELECT company_id, string_agg(employee, ', ' ORDER BY employee) FROM mytable GROUP BY company_id;
или действительно:
SELECT string_agg(actor_name, ', ' ORDER BY first_appearance)
PostgreSQL 8.4 или более поздней версии:
PostgreSQL 8.4 (в 2009 году) представил агрегатная функция
array_agg(expression)
, который объединяет значения в массив. Тогдаarray_to_string()
можно использовать для получения желаемого результата:SELECT company_id, array_to_string(array_agg(employee), ', ') FROM mytable GROUP BY company_id;
string_agg
по предварительной версии 9.0:в случае, если кто-то столкнется с этим, ища совместимую прокладку для баз данных pre-9.0, можно реализовать все в
string_agg
за исключением элементORDER BY
предложения.так что с приведенным ниже определением это должно работать так же, как и в 9.x Postgres DB:
SELECT string_agg(name, '; ') AS semi_colon_separated_names FROM things;
но это будет синтаксическая ошибка:
SELECT string_agg(name, '; ' ORDER BY name) AS semi_colon_separated_names FROM things; --> ERROR: syntax error at or near "ORDER"
протестировано на PostgreSQL 8.3.
CREATE FUNCTION string_agg_transfn(text, text, text) RETURNS text AS $$ BEGIN IF IS NULL THEN RETURN ; ELSE RETURN || || ; END IF; END; $$ LANGUAGE plpgsql IMMUTABLE COST 1; CREATE AGGREGATE string_agg(text, text) ( SFUNC=string_agg_transfn, STYPE=text );
пользовательские вариации (все версии Postgres)
до 9.0 не было встроенной агрегатной функции для объединения строк. Самая простая пользовательская реализация (предложил Вайда Габо в этом списке рассылки пост, среди многих других) является использование встроенного
textcat
функция (которая лежит за||
оператор):CREATE AGGREGATE textcat_all( basetype = text, sfunc = textcat, stype = text, initcond = '' );
здесь
CREATE AGGREGATE
документация.это просто склеивает все строки вместе, без разделения. Чтобы получить"," вставленный между ними, не имея его в конце, вы можете сделать свою собственную функцию конкатенации и заменить ее на "textcat" выше. Здесь я собрал и проверено на 8.3.12:
CREATE FUNCTION commacat(acc text, instr text) RETURNS text AS $$ BEGIN IF acc IS NULL OR acc = '' THEN RETURN instr; ELSE RETURN acc || ', ' || instr; END IF; END; $$ LANGUAGE plpgsql;
эта версия будет выводить запятую, даже если значение в строке равно null или пусто, поэтому вы получаете вывод следующим образом:
a, b, c, , e, , g
если вы хотите удалить лишние запятые, чтобы вывести это:
a, b, c, e, g
добавьте
ELSIF
проверьте функцию следующим образом:CREATE FUNCTION commacat_ignore_nulls(acc text, instr text) RETURNS text AS $$ BEGIN IF acc IS NULL OR acc = '' THEN RETURN instr; ELSIF instr IS NULL OR instr = '' THEN RETURN acc; ELSE RETURN acc || ', ' || instr; END IF; END; $$ LANGUAGE plpgsql;
Как насчет использования встроенных функций массива Postgres? По крайней мере, на 8.4 это работает из коробки:
SELECT company_id, array_to_string(array_agg(employee), ',') FROM mytable GROUP BY company_id;
как из PostgreSQL 9.0 вы можете использовать агрегатную функцию под названием string_agg. Ваш новый SQL должен выглядеть примерно так:
SELECT company_id, string_agg(employee, ', ') FROM mytable GROUP BY company_id;
Я не претендую на кредит для ответа, потому что я нашел его после некоторого поиска:
Я не знал, что PostgreSQL позволяет вам определять свои собственные агрегатные функции с помощью СОЗДАТЬ АГРЕГАТ
этот пост в списке PostgreSQL показано, как тривиально создать функцию, чтобы сделать то, что требуется:
CREATE AGGREGATE textcat_all( basetype = text, sfunc = textcat, stype = text, initcond = '' ); SELECT company_id, textcat_all(employee || ', ') FROM mytable GROUP BY company_id;
подробности на французском языке):
CREATE OR REPLACE FUNCTION concat2(text, text) RETURNS text AS ' SELECT CASE WHEN IS NULL OR = \'\' THEN WHEN IS NULL OR = \'\' THEN ELSE || \' / \' || END; ' LANGUAGE SQL; CREATE AGGREGATE concatenate ( sfunc = concat2, basetype = text, stype = text, initcond = ''
);
и затем использовать его как:
SELECT company_id, concatenate(employee) AS employees FROM ...
этот последний фрагмент списка объявлений может представлять интерес, если вы будете обновляться до 8.4:
пока 8.4 не выйдет с a супер-эффективный родной, вы можете добавить функция array_accum() в Документация PostgreSQL для прокатки любой столбец в массив, который может затем используйте код приложения, или в сочетании с array_to_string () to отформатируйте его в виде списка:
Я бы связался с документами разработки 8.4, но они, похоже, еще не перечисляют эту функцию.
после ответа кэВ, с использованием базы данных Postgres документы:
сначала создайте массив элементов, затем используйте встроенный
после еще раз об использовании пользовательской агрегатной функции конкатенации строк: вам нужно помнить, что оператор select будет размещать строки в любом порядке, поэтому вам нужно будет сделать sub выберите на С выступление приказом предложение, а затем внешний выберите С группы по предложение для агрегирования строк, таким образом:
SELECT custom_aggregate(MY.special_strings) FROM (SELECT special_strings, grouping_column FROM a_table ORDER BY ordering_column) MY GROUP BY MY.grouping_column
Я нашел эту документацию PostgreSQL полезной:http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html.
в моем случае я искал простой SQL для объединения поля с скобками вокруг него, если поле не пусто.
select itemid, CASE itemdescription WHEN '' THEN itemname ELSE itemname || ' (' || itemdescription || ')' END from items;
в соответствии с версией PostgreSQL 9.0 и выше вы можете использовать агрегатную функцию string_agg. Ваш новый SQL должен выглядеть примерно так:
SELECT company_id, string_agg(employee, ', ') FROM mytable GROUP BY company_id;
вы также можете использовать функцию формат. Который также может неявно заботиться о преобразовании типа text, int и т. д. Сам по себе.
create or replace function concat_return_row_count(tbl_name text, column_name text, value int) returns integer as $row_count$ declare total integer; begin EXECUTE format('select count(*) from %s WHERE %s = %s', tbl_name, column_name, value) INTO total; return total; end; $row_count$ language plpgsql; postgres=# select concat_return_row_count('tbl_name','column_name',2); --2 is the value