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 або інший спосіб досягти цього?


Не відповідь, але перевірити postgresonline.com/journal/index.php?/archives/… .
Kuberchaun



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 повертає масив, але ви можете КАСТИТИ це на текст та редагувати за потребою (див. роз'яснення нижче).

Перед початком версії 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 () НЕ передає внутрішні результати спочатку TEXT. Отже, "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 у всіх випадках. Якщо у вашому value_field міститься вбудована кома, отриманий CSV неправильний. Використовуючи array_agg () та кастинг до TEXT, належним чином цитує рядки із вбудованими комами. Єдине застереження полягає в тому, що воно також включає в себе початкові і закінчуючі фігурні дужки, звідси і моє твердження "і редагувати за потребою". Я відредагую, щоб уточнити цей момент.
Меттью Вуд

FYI: ось посилання на документи на array_agg в 8.4
Michael Rusch

256

З 9.0 це ще простіше:

SELECT id, 
       string_agg(some_column, ',')
FROM the_table
GROUP BY id

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, ',')
Кендалл,

48
SELECT array_to_string(array(SELECT a FROM b),', ');

Зробимо також.


Чи можливо зробити щось подібне в цьому коментарі , де ви збираєтесь у певному порядку? Як би ви обробляли групування за одним стовпцем та впорядкування за іншим (наприклад, для об'єднання змінних у поздовжньому наборі даних)?
Майкл А

15

Спробуйте так:

select field1, array_to_string(array_agg(field2), ',')
from table1
group by field1;

2

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

select
  array_to_string(
    array(select distinct unnest(zip_codes) from table),
    ', '
);

Дублююча відповідь, @max_spy сказав те ж саме п’ять років тому
Emil

@ 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

Я перевірив його на rextester.com/l/postgresql_online_compiler і не працював: 42883: функція listagg (текст, невідомий, текст) не існує
Manuel Romeiro

Oracle має інший синтаксис та функції, ніж постгреси.
Герман Дж. Радтке III
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.