Как объединить строки строкового поля в запросе 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 266

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 отформатируйте его в виде списка:

http://www.postgresql.org/docs/current/static/xaggr.html

Я бы связался с документами разработки 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