PostgreSQL 'group by'쿼리에서 문자열 필드의 문자열을 연결하는 방법은 무엇입니까?


351

쿼리로 그룹 내의 필드 문자열을 연결하는 방법을 찾고 있습니다. 예를 들어 테이블이 있습니다.

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 을 수행하는 내장 함수가 있습니다.


1
Markus Döring의 답변은 기술적으로 더 좋습니다.
pstanton

@pstanton, Döring의 답변은 8.4 이하에서만 더 좋습니다.
Jared Beck

이 질문은 dba.stackexchange.com에 더 적합한 것으로 보입니다 .
Dave Jarvis

이것은 유효한 답변이어야합니다. stackoverflow.com/a/47638417/243233
Jus12

답변:


542

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 8.4 이전 버전의 경우 :

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 $1 IS NULL THEN
                RETURN $2;
            ELSE
                RETURN $1 || $3 || $2;
            END IF;
        END;
    $$
    LANGUAGE plpgsql IMMUTABLE
COST 1;

CREATE AGGREGATE string_agg(text, text) (
    SFUNC=string_agg_transfn,
    STYPE=text
);

맞춤형 변형 (모든 Postgres 버전)

9.0 이전에는 문자열을 연결하는 내장 집계 함수가 없었습니다. Vajda Gabo가이 메일 링리스트 post에서 제안한 가장 간단한 사용자 정의 구현 은 내장 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;

1
varchar를 텍스트로 S & R해야했습니다 (최신 pgsql 안정). 그러나 이것은 훌륭합니다!
Kev

1
SQL로만 함수를 작성할 수 있으므로 설치가 더 쉽습니다 (plpgsql은 수퍼 유저가 설치해야 함). 예를 들어 내 게시물을 참조하십시오.
bortzmeyer

11
"문자열을 연결하는 내장 집계 함수가 없습니다"-왜 사용하지 array_to_string(array_agg(employee), ',')않습니까?
pstanton

2
PostgreSQL 9.0 기능의 경우 +1 9.0 이전 버전에 대해 걱정해야하는 경우 Markus의 답변이 더 좋습니다.
브래드 코흐

7
최근 Postgres 버전 Order By에서는 집계 함수 내부에 절을 허용합니다 . 예 :string_agg(employee, ',' Order By employee)
IMSoP

98

Postgres 내장 배열 함수를 사용하는 것은 어떻습니까? 최소한 8.4에서 이것은 즉시 작동합니다.

SELECT company_id, array_to_string(array_agg(employee), ',')
FROM mytable
GROUP BY company_id;

슬프게도 Greenplum (v8.2)에서는 작동하지 않습니다. +1 모두 동일
ekkis

Greenplum 4.3.4.1 (PostgreSQL 8.2.15 기반)에서 제대로 작동합니다.
PhilHibbs

19

PostgreSQL 9.0부터 string_agg 라는 집계 함수를 사용할 수 있습니다 . 새 SQL은 다음과 같아야합니다.

SELECT company_id, string_agg(employee, ', ')
FROM mytable
GROUP BY company_id;


13

검색 후 답을 찾았 기 때문에 답변에 대한 크레딧이 없다고 주장합니다.

내가 모르는 것은 PostgreSQL이 CREATE AGGREGATE 를 사용하여 자신의 집계 함수를 정의 할 수 있다는 것입니다

PostgreSQL 목록 의이 게시물 은 필요한 작업을 수행하는 함수를 만드는 것이 얼마나 간단한지를 보여줍니다.

CREATE AGGREGATE textcat_all(
  basetype    = text,
  sfunc       = textcat,
  stype       = text,
  initcond    = ''
);

SELECT company_id, textcat_all(employee || ', ')
FROM mytable
GROUP BY company_id;

7

이미 언급했듯이, 자신 만의 집계 함수를 만드는 것이 옳은 일입니다. 내 연결 집계 함수는 다음과 같습니다 ( 프랑스어로 세부 정보를 찾을 수 있음 ).

CREATE OR REPLACE FUNCTION concat2(text, text) RETURNS text AS '
    SELECT CASE WHEN $1 IS NULL OR $1 = \'\' THEN $2
            WHEN $2 IS NULL OR $2 = \'\' THEN $1
            ELSE $1 || \' / \' || $2
            END; 
'
 LANGUAGE SQL;

CREATE AGGREGATE concatenate (
  sfunc = concat2,
  basetype = text,
  stype = text,
  initcond = ''

);

그런 다음 다음과 같이 사용하십시오.

SELECT company_id, concatenate(employee) AS employees FROM ...

5

8.4로 업그레이드 할 경우이 최신 공지 목록 스 니펫이 유용 할 수 있습니다.

8.4에서 매우 효과적인 네이티브 함수가 나올 때까지 PostgreSQL 설명서에서 array_accum () 함수를 추가하여 열을 배열로 롤업 할 수 있습니다. 그런 다음 응용 프로그램 코드에서 사용하거나 array_to_string ()과 함께 형식을 지정할 수 있습니다 목록으로 :

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

8.4 개발 문서에 링크했지만 아직이 기능을 나열하지 않은 것 같습니다.


5

Postgres 문서를 사용하여 Kev의 답변에 후속 :

먼저 요소의 배열을 만든 다음 내장 array_to_string함수 를 사용하십시오 .

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

select array_to_string(array_accum(name),'|') from table group by id;

5

문자열 연결의 사용자 정의 집계 함수의 사용에 다시 한번 다음은 당신이 선택 사항이이 서브 할 필요가 있으므로, 임의의 순서로 행을 배치 것을 기억해야 선택 에서 에서 와 문 에 의해 순서 절하고, 그런 다음 문자열을 집계하기 위해 group by 절을 사용 하여 외부를 선택하십시오 .

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

3

이 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;


0

PostgreSQL 9.0 이상 버전에 따르면 string_agg라는 집계 함수를 사용할 수 있습니다. 새 SQL은 다음과 같아야합니다.

SELECT company_id, string_agg(employee, ', ')
    FROM mytable GROUP BY company_id;

0

포맷 기능을 사용할 수도 있습니다. 텍스트, 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

1
이것은 문자열 값을 연결하기 위해 집계를 사용하는 것과 어떻게 관련이 있습니까?
a_horse_with_no_name 11:30

0

Jetbrains Rider를 사용하고 있으며 JSON에서 모두 래핑하는 것처럼 보이므로 위 예제의 결과를 다시 실행하기가 번거로 웠습니다. 이것은 실행하기 쉬운 단일 명령문으로 결합합니다.

select string_agg('drop table if exists "' || tablename || '" cascade', ';') 
from pg_tables where schemaname != $$pg_catalog$$ and tableName like $$rm_%$$

0

string_agg가 지원되지 않는 Amazon Redshift를 사용하는 경우 listagg를 사용해보십시오.

SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.