Postgresql GROUP_CONCAT эквивалент?

248

У меня есть таблица, и я хотел бы получить одну строку на идентификатор с объединенными значениями поля.

В моей таблице, например, у меня есть это:

TM67 | 4  | 32556
TM67 | 9  | 98200
TM67 | 72 | 22300
TM99 | 2  | 23009
TM99 | 3  | 11200

И я хотел бы вывести:

TM67 | 4,9,72 | 32556,98200,22300
TM99 | 2,3    | 23009,11200

В MySQL я был в состоянии использовать агрегатную функцию GROUP_CONCAT, но здесь это не сработало ... Есть ли эквивалент для PostgreSQL или другой способ сделать это?

TwixxyKit
источник
Не ответ, но посмотрите postgresonline.com/journal/index.php?/archives/… .
Kuberchaun
1
stackoverflow.com/questions/1943433/…
Милен А. Радев
возможный дубликат функции MySQL group_concat group в SQL Server?
ntalbs
1
Я думаю, что лучший ответ все еще в другом вопросе: stackoverflow.com/a/47638417/243233
Jus12

Ответы:

237

Вероятно, это хорошая отправная точка (только версия 8.4+):

SELECT id_field, array_agg(value_field1), array_agg(value_field2)
FROM data_table
GROUP BY id_field

array_agg возвращает массив, но вы можете CAST его в текст и редактировать по мере необходимости (см. пояснения ниже).

До версии 8.4 вы должны определить ее самостоятельно перед использованием:

CREATE AGGREGATE array_agg (anyelement)
(
    sfunc = array_append,
    stype = anyarray,
    initcond = '{}'
);

(перефразировано из документации PostgreSQL)

Разъяснения:

  • Результатом преобразования массива в текст является то, что результирующая строка начинается и заканчивается фигурными скобками. Эти скобки должны быть удалены каким-либо методом, если они нежелательны.
  • Приведение ANYARRAY к TEXT лучше всего имитирует вывод CSV, поскольку элементы, содержащие встроенные запятые, заключаются в двойные кавычки в выводе в стандартном стиле CSV. Ни array_to_string (), ни string_agg () (функция «group_concat», добавленная в 9.1) не заключают строки в кавычки, что приводит к неверному количеству элементов в результирующем списке.
  • Новая функция 9.1 string_agg () НЕ приводит вначале внутренние результаты к ТЕКСТУ. Таким образом, «string_agg (value_field)» выдаст ошибку, если value_field является целым числом. "string_agg (value_field :: text)" будет необходимо. Для метода array_agg () требуется только одно приведение после агрегации (а не приведение к значению).
Мэтью Вуд
источник
1
А в 9.0 у вас будет listagg ()
Скотт Бэйли
6
Для получения CSV запрос должен быть: SELECT id_field, array_to_string (array_agg (value_field1), ','), array_to_string (array_agg (value_field2), ',') FROM data_table GROUP BY id_field
Nux
2
Вы не можете использовать array_to_string во всех случаях здесь. Если ваше поле значений содержит встроенную запятую, результирующий CSV будет неправильным. Использование array_agg () и приведение к TEXT приводит к правильному заключению строк в кавычки. Единственное предостережение в том, что оно также включает в себя начальные и конечные фигурные скобки, отсюда и мое утверждение «и редактировать по мере необходимости». Я буду редактировать, чтобы прояснить этот момент.
Мэтью Вуд
К вашему сведению: вот ссылка на документы на array_agg в 8.4
Майкл Раш
256

С 9.0 это еще проще:

SELECT id, 
       string_agg(some_column, ',')
FROM the_table
GROUP BY id
a_horse_with_no_name
источник
32
Обратите внимание, что синтаксис также позволяет указывать порядок значений в строке (или в массиве, используя array_agg), например string_agg(some_column, ',' ORDER BY some_column)или дажеstring_agg(surname || ', ' || forename, '; ' ORDER BY surname, forename)
IMSoP
8
distinctЗамечательно, что работает с string_agg, так что можно использоватьstring_agg(distinct some_solumn, ',')
arun
3
Обратите внимание, что вам может потребоваться TEXTпреобразовать значение столбца, если оно не является строковым значением (т. Е. uuid). Это будет выглядетьstring_agg(some_column::text, ',')
Kendall
48
SELECT array_to_string(array(SELECT a FROM b),', ');

Будет так же хорошо.

genobis
источник
Можно ли сделать что-то подобное в этом комментарии , где вы агрегируете в определенном порядке? Как бы вы справились с группировкой по одному столбцу и упорядочением по другому (например, для объединения переменных в продольном наборе данных)?
Майкл А
15

Попробуйте вот так:

select field1, array_to_string(array_agg(field2), ',')
from table1
group by field1;
max_spy
источник
2

и версия для работы с типом массива :

select
  array_to_string(
    array(select distinct unnest(zip_codes) from table),
    ', '
);
Славомир Ленарт
источник
Двойной ответ, @max_spy сказал то же самое пять лет назад
Эмиль Викстрём,
@ EmilVikström: вы имеете право ошибаться, но читайте внимательно. Это не только отличается, но я привел пример, который работает с типом массива, например, zip_codes character varying(5)[]. Кроме того, я проверил, что для моей цели - нужно гнестить, иначе вы увидите ERROR: cannot accumulate arrays of different dimensionality.
Славомир Ленарт
1

Мое предложение в postgresql

SELECT cpf || ';' || nome || ';' || telefone  
FROM (
      SELECT cpf
            ,nome
            ,STRING_AGG(CONCAT_WS( ';' , DDD_1, TELEFONE_1),';') AS telefone 
      FROM (
            SELECT DISTINCT * 
            FROM temp_bd 
            ORDER BY cpf DESC ) AS y
      GROUP BY 1,2 ) AS x   
Лукас Кабрал
источник
1
Почему вы делаете ORDER BYво внутреннем запросе? В любом случае заказ не потеряется?
mypetlion
-1

Надеюсь, что ниже Oracle запрос будет работать.

Select First_column,LISTAGG(second_column,',') 
    WITHIN GROUP (ORDER BY second_column) as Sec_column, 
    LISTAGG(third_column,',') 
    WITHIN GROUP (ORDER BY second_column) as thrd_column 
FROM tablename 
GROUP BY first_column
kiruba
источник
Я протестировал его на rextester.com/l/postgresql_online_compiler и не работал: 42883: функция listagg (текст, неизвестный, текст) не существует
Мануэль Ромейро,
Oracle имеет другой синтаксис и функции, чем postgres.
Герман Дж. Радтке III