PostgreSQLの「グループ化」クエリで文字列フィールドの文字列を連結する方法は?


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
MarkusDöringの答えは技術的に優れています。
プスタントン2011

@ 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が提案したものなど)は、組み込み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安定版)、これは素晴らしいです!
ケブ

1
関数はSQLのみで記述でき、インストールが簡単です(plpgsqlはスーパーユーザーがインストールする必要があります)。例については、私の投稿を参照してください。
bortzmeyer 2008

11
「文字列を連結する組み込みの集約関数はありません」-なぜ使用しないのですarray_to_string(array_agg(employee), ',')か?
プスタントン2011

2
PostgreSQL 9.0関数の場合は+1。9.0より前のバージョンについて心配する必要がある場合は、Markusの答えが適しています。
Brad Koch

7
Postgresの最近のバージョンではOrder By、集約関数内の句も許可されていることに注意してください。たとえばstring_agg(employee, ',' Order By employee)
IMSoP

99

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 2017年

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

さらに、文字列連結のカスタム集計関数の使用について説明します。selectステートメントは行を任意の順序で配置することを覚えておく必要があるためfromステートメントでorder by句を使用してサブ選択を行う必要があります。次に、文字列を集計するための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

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.