Wie verkette ich Zeichenfolgen eines Zeichenfolgenfelds in einer PostgreSQL-Abfrage „Gruppieren nach“?

StackOverflow https://stackoverflow.com/questions/43870

Frage

Ich suche nach einer Möglichkeit, die Zeichenfolgen eines Felds innerhalb einer Gruppe per Abfrage zu verketten.So habe ich zum Beispiel eine Tabelle:

ID   COMPANY_ID   EMPLOYEE
1    1            Anna
2    1            Bill
3    2            Carol
4    2            Dave

und ich wollte nach Firmen-ID gruppieren, um so etwas zu erhalten:

COMPANY_ID   EMPLOYEE
1            Anna, Bill
2            Carol, Dave

Hierzu gibt es in MySQL eine integrierte Funktion group_concat

War es hilfreich?

Lösung

PostgreSQL 9.0 oder höher:

Aktuelle Versionen von Postgres (seit Ende 2010) verfügen über die string_agg(expression, delimiter) Funktion, die genau das tut, wonach die Frage gefragt hat, und Ihnen sogar die Angabe der Trennzeichenfolge ermöglicht:

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

Postgres 9.0 hat außerdem die Möglichkeit hinzugefügt, eine anzugeben ORDER BY Klausel in jedem Aggregatausdruck;andernfalls ist die Reihenfolge undefiniert.So können Sie jetzt schreiben:

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

Oder tatsächlich:

SELECT string_agg(actor_name, ', ' ORDER BY first_appearance)

PostgreSQL 8.4 oder höher:

PostgreSQL 8.4 (im Jahr 2009) eingeführt die Aggregatfunktion array_agg(expression) Dadurch werden die Werte in einem Array verkettet.Dann array_to_string() kann verwendet werden, um das gewünschte Ergebnis zu erzielen:

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

string_agg für Versionen vor 9.0:

Falls jemand auf der Suche nach einem Kompatibilitäts-Shim für Datenbanken vor 9.0 darauf stößt, ist es möglich, alles darin zu implementieren string_agg außer der ORDER BY Klausel.

Mit der folgenden Definition sollte dies also genauso funktionieren wie in einer 9.x-Postgres-Datenbank:

SELECT string_agg(name, '; ') AS semi_colon_separated_names FROM things;

Dies wird jedoch ein Syntaxfehler sein:

SELECT string_agg(name, '; ' ORDER BY name) AS semi_colon_separated_names FROM things;
--> ERROR: syntax error at or near "ORDER"

Getestet auf 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
);

Benutzerdefinierte Variationen (alle Postgres-Versionen)

Vor 9.0 gab es keine integrierte Aggregatfunktion zum Verketten von Zeichenfolgen.Die einfachste benutzerdefinierte Implementierung (vorgeschlagen von Vajda Gabo in diesem Mailinglistenbeitrag, neben vielen anderen) ist die Verwendung der integrierten textcat Funktion (die hinter der liegt || Operator):

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

Hier ist das CREATE AGGREGATE Dokumentation.

Dadurch werden einfach alle Saiten zusammengeklebt, ohne Trennzeichen.Um ein „,“ dazwischen einzufügen, ohne es am Ende zu haben, möchten Sie vielleicht Ihre eigene Verkettungsfunktion erstellen und diese durch das „textcat“ oben ersetzen.Hier ist eines, das ich am 8.3.12 zusammengestellt und getestet habe:

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;

Diese Version gibt ein Komma aus, auch wenn der Wert in der Zeile null oder leer ist. Sie erhalten also eine Ausgabe wie diese:

a, b, c, , e, , g

Wenn Sie zusätzliche Kommas entfernen möchten, um Folgendes auszugeben:

a, b, c, e, g

Fügen Sie dann eine hinzu ELSIF Überprüfen Sie die Funktion wie folgt:

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;

Andere Tipps

Wie wäre es mit der Verwendung der in Postgres integrierten Array-Funktionen?Zumindest unter 8.4 funktioniert das sofort:

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

Ab PostgreSQL 9.0 können Sie die aufgerufene Aggregatfunktion verwenden string_agg.Ihr neues SQL sollte etwa so aussehen:

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

Ich beanspruche keine Anerkennung für die Antwort, da ich sie nach einigem Suchen gefunden habe:

Was ich nicht wusste ist, dass Sie mit PostgreSQL Ihre eigenen Aggregatfunktionen definieren können Aggregat erstellen

Dieser Beitrag in der PostgreSQL-Liste zeigt, wie trivial es ist, eine Funktion zu erstellen, die das tut, was erforderlich ist:

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

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

Wie bereits erwähnt, ist es richtig, eine eigene Aggregatfunktion zu erstellen.Hier ist meine Verkettungsaggregatfunktion (Sie finden Einzelheiten auf Französisch):

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 = ''

);

Und dann verwenden Sie es als:

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

Dieser aktuelle Ausschnitt aus der Ankündigungsliste könnte für Sie von Interesse sein, wenn Sie ein Upgrade auf 8.4 durchführen:

Bis 8.4 mit einem supereffizienten nativen, können Sie Die Funktion array_accum() in der PostgreSQL-Dokumentation für das Rollen eine beliebige Spalte in ein Array einfügen, das dann vom Anwendungscode verwendet werden, oder kombiniert mit array_to_string() Formatieren Sie es als Liste:

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

Ich würde auf die 8.4-Entwicklungsdokumente verlinken, aber dort scheint diese Funktion noch nicht aufgeführt zu sein.

Weiterverfolgung von Kevs Antwort unter Verwendung der Postgres-Dokumente:

Erstellen Sie zunächst ein Array der Elemente und verwenden Sie dann das integrierte Array array_to_string Funktion.

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

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

Nachfolgend noch einmal die Verwendung einer benutzerdefinierten Aggregatfunktion zur Zeichenfolgenverkettung:Sie müssen bedenken, dass die SELECT-Anweisung Zeilen in beliebiger Reihenfolge platziert, daher müssen Sie eine Sub-Anweisung ausführen wählen im aus Aussage mit einem Sortieren nach Klausel und dann eine äußere wählen mit einem gruppiere nach -Klausel zum Aggregieren der Zeichenfolgen, also:

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

Ich fand diese PostgreSQL-Dokumentation hilfreich: http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html.

In meinem Fall habe ich nach einfachem SQL gesucht, um ein Feld mit Klammern zu verketten, wenn das Feld nicht leer ist.

select itemid, 
  CASE 
    itemdescription WHEN '' THEN itemname 
    ELSE itemname || ' (' || itemdescription || ')' 
  END 
from items;

Verwenden STRING_AGG Funktion für PostgreSQL Und Google BigQuery SQL:

SELECT company_id, STRING_AGG(employee, ', ')
FROM employees
GROUP BY company_id;

Ab Version PostgreSQL 9.0 können Sie die Aggregatfunktion namens string_agg verwenden.Ihr neues SQL sollte etwa so aussehen:

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

Sie können auch die Formatierungsfunktion verwenden.Dies kann auch implizit die Typkonvertierung von Text, Int usw. selbst übernehmen.

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

Ich verwende Jetbrains Rider und es war mühsam, die Ergebnisse aus den obigen Beispielen zu kopieren, um sie erneut auszuführen, da es schien, als würde alles in JSON verpackt.Dadurch werden sie zu einer einzigen Anweisung zusammengefügt, die einfacher auszuführen ist

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

Wenn Sie Amazon Redshift verwenden und string_agg nicht unterstützt wird, versuchen Sie es mit listagg.

SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top