GROUP_CONCAT, STRING_AGG und LISTAGG im Vergleich
String-Aggregation fasst mehrere Zeilen einer Gruppe zu einer einzigen, kommagetrennten Textliste zusammen, etwa alle Tags eines Produkts in einer Zeile statt in mehreren Zeilen. MySQL nutzt dafür GROUP_CONCAT, PostgreSQL und SQL Server STRING_AGG, Oracle LISTAGG. Dieser Artikel zeigt Syntax, Reihenfolge, Trennzeichen und Längenlimits aller vier Systeme im direkten Vergleich.
Inhaltsverzeichnis
- 1. Warum String-Aggregation ein eigenes Thema ist
- 2. GROUP_CONCAT in MySQL: Syntax und Optionen
- 3. STRING_AGG in PostgreSQL und SQL Server
- 4. LISTAGG in Oracle und WITHIN GROUP
- 5. Deterministische Reihenfolge in String-Aggregation erzwingen
- 6. Längenlimits: group_concat_max_len und Overflow-Fehler
- 7. String-Aggregation kombiniert mit GROUP BY im Reporting
- 8. Performance: String-Aggregation in der Datenbank vs. Anwendung
- 9. String-Aggregation im Systemvergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum String-Aggregation ein eigenes Thema ist
String-Aggregation löst ein Problem, das klassische Aggregatfunktionen wie SUM oder COUNT nicht abdecken können: Statt einer Zahl soll pro Gruppe eine zusammengesetzte Textliste entstehen, etwa alle Tags eines Produkts, alle E-Mail-Adressen eines Teams oder alle Bestellpositionen einer Rechnung in einer einzigen Zelle. Ohne String-Aggregation müsste diese Zusammenfassung entweder in der Anwendungsschicht nach dem Abruf mehrerer Zeilen erfolgen, oder man verzichtet ganz auf die kompakte Darstellung und zeigt jede Zeile einzeln.
Der praktische Nutzen von String-Aggregation zeigt sich vor allem in Reports, CSV-Exporten und Übersichtstabellen, in denen eine 1:n-Beziehung platzsparend in einer einzigen Spalte dargestellt werden soll. Ein Bericht über Bestellungen, der pro Bestellung alle enthaltenen Produktnamen in einer Spalte auflistet, ist ein alltägliches Beispiel. Ohne String-Aggregation würde dieselbe Bestellung einmal pro Produktposition als separate Zeile erscheinen, was die Lesbarkeit des Reports erheblich verschlechtert.
Jedes der großen relationalen Datenbanksysteme bietet String-Aggregation an, allerdings mit unterschiedlichem Funktionsnamen und leicht abweichender Syntax. MySQL verwendet GROUP_CONCAT, PostgreSQL und SQL Server STRING_AGG, Oracle LISTAGG. Dieser Artikel geht alle vier Varianten durch und zeigt, worauf bei Reihenfolge, Trennzeichen und Längenbegrenzung jeweils zu achten ist.
2. GROUP_CONCAT in MySQL: Syntax und Optionen
GROUP_CONCAT ist die MySQL-eigene Implementierung von String-Aggregation und gehört zu den am häufigsten genutzten Funktionen in MySQL-Reports. Die Grundsyntax kombiniert eine beliebige Ausdrucksspalte mit optionalen Zusätzen für Trennzeichen, Sortierung und Duplikatfilterung. Ohne weitere Angaben verwendet GROUP_CONCAT standardmäßig ein Komma als Trennzeichen zwischen den einzelnen Werten.
Besonders praktisch ist die Kombination aus DISTINCT, ORDER BY und SEPARATOR innerhalb derselben Funktion. DISTINCT entfernt doppelte Werte vor der Zusammenfassung, ORDER BY bestimmt die Reihenfolge der Werte innerhalb der aggregierten Zeichenkette, und SEPARATOR legt ein beliebiges eigenes Trennzeichen fest, etwa ein Semikolon oder einen Zeilenumbruch. Diese drei Optionen zusammen machen GROUP_CONCAT zu einer der flexibelsten Implementierungen von String-Aggregation überhaupt.
-- String aggregation with GROUP_CONCAT in MySQL
SELECT
p.product_id,
p.product_name,
GROUP_CONCAT(
DISTINCT t.tag_name
ORDER BY t.tag_name ASC
SEPARATOR ', '
) AS tag_list
FROM products p
JOIN product_tags pt ON pt.product_id = p.product_id
JOIN tags t ON t.tag_id = pt.tag_id
GROUP BY p.product_id, p.product_name;
-- Result (excerpt)
-- product_id | product_name | tag_list
-- 1 | USB-C Kabel | Elektronik, Kabel, USB
3. STRING_AGG in PostgreSQL und SQL Server
PostgreSQL und SQL Server implementieren String-Aggregation unter demselben Funktionsnamen STRING_AGG, allerdings mit einem strukturellen Unterschied zu GROUP_CONCAT. Bei STRING_AGG ist der Separator kein optionaler Zusatz, sondern ein zwingender zweiter Parameter der Funktion selbst, was Tippfehler wie ein vergessenes Trennzeichen von vornherein ausschließt. Die grundlegende Signatur lautet STRING_AGG(ausdruck, trennzeichen), wobei der Ausdruck vor der Zusammenfassung typischerweise noch in Text umgewandelt werden muss, falls er nicht bereits vom Typ Text ist.
Für Duplikatfilterung wird bei STRING_AGG in PostgreSQL ein DISTINCT direkt vor dem Ausdruck platziert, ähnlich wie bei anderen Aggregatfunktionen. Die Sortierung erfolgt über ein optionales ORDER BY innerhalb der Funktion, syntaktisch dem GROUP_CONCAT-Ansatz aus MySQL sehr ähnlich. SQL Server unterstützt STRING_AGG seit Version 2017 und WITHIN GROUP für die Sortierung erst seit dieser Version zusätzlich zur Grundfunktion, was bei älteren SQL-Server-Installationen zu berücksichtigen ist.
-- String aggregation with STRING_AGG in PostgreSQL
SELECT
p.product_id,
p.product_name,
STRING_AGG(
DISTINCT t.tag_name, ', '
ORDER BY t.tag_name ASC
) AS tag_list
FROM products p
JOIN product_tags pt ON pt.product_id = p.product_id
JOIN tags t ON t.tag_id = pt.tag_id
GROUP BY p.product_id, p.product_name;
-- Equivalent in SQL Server (2017+), sorting via WITHIN GROUP
SELECT
p.product_id,
p.product_name,
STRING_AGG(t.tag_name, ', ') WITHIN GROUP (ORDER BY t.tag_name ASC) AS tag_list
FROM products p
JOIN product_tags pt ON pt.product_id = p.product_id
JOIN tags t ON t.tag_id = pt.tag_id
GROUP BY p.product_id, p.product_name;
4. LISTAGG in Oracle und WITHIN GROUP
Oracle implementiert String-Aggregation unter dem Namen LISTAGG, das konzeptionell näher an STRING_AGG als an GROUP_CONCAT liegt. Die Sortierung erfolgt über die WITHIN GROUP-Klausel, dieselbe Syntax, die SQL Server für STRING_AGG übernommen hat, was den Umstieg zwischen beiden Systemen erleichtert. Ohne WITHIN GROUP ist die Reihenfolge der Werte in LISTAGG technisch nicht definiert, auch wenn in der Praxis oft die physische Speicherreihenfolge zu beobachten ist.
Ein Detail, das bei LISTAGG in Oracle häufig übersehen wird: Die Funktion unterstützt in älteren Versionen kein natives DISTINCT für die Duplikatfilterung, sodass Duplikate vor dem Aufruf über eine Subquery mit SELECT DISTINCT entfernt werden müssen. Erst neuere Oracle-Versionen bieten mit LISTAGG(DISTINCT ...) eine direkte Lösung. Diese Einschränkung ist ein gutes Beispiel dafür, dass String-Aggregation trotz ähnlicher Grundidee zwischen den Systemen nicht eins zu eins übertragbar ist.
-- String aggregation with LISTAGG in Oracle
SELECT
p.product_id,
p.product_name,
LISTAGG(t.tag_name, ', ') WITHIN GROUP (ORDER BY t.tag_name ASC) AS tag_list
FROM products p
JOIN product_tags pt ON pt.product_id = p.product_id
JOIN tags t ON t.tag_id = pt.tag_id
GROUP BY p.product_id, p.product_name;
-- Older Oracle versions: deduplicate via subquery before LISTAGG
SELECT product_id, product_name,
LISTAGG(tag_name, ', ') WITHIN GROUP (ORDER BY tag_name) AS tag_list
FROM (
SELECT DISTINCT p.product_id, p.product_name, t.tag_name
FROM products p
JOIN product_tags pt ON pt.product_id = p.product_id
JOIN tags t ON t.tag_id = pt.tag_id
)
GROUP BY product_id, product_name;
5. Deterministische Reihenfolge in String-Aggregation erzwingen
Ein häufiger Fehler bei String-Aggregation ist die Annahme, die Reihenfolge der zusammengefassten Werte entspreche automatisch einer sinnvollen Sortierung. Ohne explizites ORDER BY innerhalb der Aggregatfunktion ist die Reihenfolge in vielen Systemen technisch nicht garantiert, auch wenn sie sich in Tests scheinbar konsistent verhält. Genau diese scheinbare Konsistenz ist tückisch, weil sie nach einem Datenbank-Upgrade, einer geänderten Indexstruktur oder einer Parallelverarbeitung plötzlich abweichen kann, ohne dass sich an der eigentlichen Abfrage etwas geändert hätte.
Die zuverlässige Lösung ist immer ein explizites ORDER BY innerhalb der jeweiligen Aggregatfunktion, sei es als eigener Klausel-Teil bei GROUP_CONCAT und STRING_AGG in PostgreSQL, oder als WITHIN GROUP bei STRING_AGG in SQL Server und LISTAGG in Oracle. Wer für einen Report reproduzierbare, alphabetisch oder nach Erstellungsdatum sortierte Listen benötigt, sollte diese Sortierung nie dem Zufall der internen Speicherreihenfolge überlassen, sondern immer explizit in der String-Aggregation angeben.
6. Längenlimits: group_concat_max_len und Overflow-Fehler
String-Aggregation erzeugt aus mehreren Zeilen eine einzige Zeichenkette, deren Länge mit der Anzahl der aggregierten Werte wächst. Bei großen Gruppen, etwa allen Bestellpositionen eines langjährigen Kunden, kann diese Zeichenkette interne Längenlimits überschreiten. MySQL begrenzt das Ergebnis von GROUP_CONCAT standardmäßig über die Session-Variable group_concat_max_len, deren Standardwert oft überraschend niedrig ist und Ergebnisse stillschweigend abschneidet, statt einen Fehler zu werfen.
PostgreSQL und SQL Server begrenzen STRING_AGG dagegen implizit über die maximale Länge des zugrunde liegenden Textdatentyps, was in der Praxis selten zum Problem wird, aber bei extrem großen Gruppen trotzdem beachtet werden sollte. Oracle wirft bei LISTAGG einen expliziten Fehler, sobald die kombinierte Zeichenkette das interne Limit von 4000 Byte überschreitet, statt das Ergebnis stillschweigend zu kürzen. Wer mit sehr großen Gruppen arbeitet, sollte group_concat_max_len in MySQL bewusst erhöhen und bei Oracle die neuere ON OVERFLOW-Klausel von LISTAGG nutzen, um Fehler kontrolliert abzufangen.
-- MySQL: raise the length limit for large string aggregation results
SET SESSION group_concat_max_len = 1000000;
SELECT
customer_id,
GROUP_CONCAT(order_id ORDER BY order_date SEPARATOR ', ') AS order_ids
FROM orders
GROUP BY customer_id;
-- Oracle: handle overflow explicitly instead of raising an error
SELECT
customer_id,
LISTAGG(order_id, ', ' ON OVERFLOW TRUNCATE '...' WITH COUNT)
WITHIN GROUP (ORDER BY order_date) AS order_ids
FROM orders
GROUP BY customer_id;
7. String-Aggregation kombiniert mit GROUP BY im Reporting
String-Aggregation entfaltet ihren praktischen Nutzen fast immer in Kombination mit GROUP BY über mehrere Dimensionsspalten. Ein typischer Report kombiniert numerische Kennzahlen wie SUM oder COUNT mit einer String-Aggregation der beteiligten Detailwerte, sodass Reporting-Nutzer in einer einzigen Zeile sowohl die Zusammenfassung als auch die Details sehen. Ein Bericht über Bestellungen pro Kunde kann so gleichzeitig die Gesamtsumme, die Anzahl der Bestellungen und eine kommagetrennte Liste der Produktkategorien zeigen, ganz ohne separate Detailabfrage.
Wichtig bei dieser Kombination ist, dass String-Aggregation und klassische Aggregatfunktionen wie SUM in derselben GROUP-BY-Gruppierung arbeiten, also dieselben Gruppierungsspalten verwenden. Werden versehentlich zusätzliche Spalten in GROUP BY aufgenommen, entstehen mehr, feinere Gruppen als beabsichtigt, und die String-Aggregation liefert pro Zeile weniger zusammengefasste Werte, als der Report eigentlich erwartet. Diese feine Wechselwirkung zwischen Gruppierungsgranularität und String-Aggregation ist eine häufige Fehlerquelle in komplexeren Reports.
-- String aggregation combined with numeric aggregates in one report row
SELECT
c.customer_id,
c.customer_name,
COUNT(DISTINCT o.order_id) AS order_count,
SUM(o.total_amount) AS total_revenue,
GROUP_CONCAT(DISTINCT cat.category_name ORDER BY cat.category_name SEPARATOR ', ') AS categories
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id
JOIN order_items oi ON oi.order_id = o.order_id
JOIN categories cat ON cat.category_id = oi.category_id
GROUP BY c.customer_id, c.customer_name;
8. Performance: String-Aggregation in der Datenbank vs. Anwendung
String-Aggregation direkt in SQL auszuführen ist in fast allen Fällen effizienter, als Rohdaten zeilenweise an die Anwendung zu übertragen und dort zusammenzufügen. Der Grund liegt im reduzierten Netzwerk-Overhead: Statt tausend Zeilen mit Tag-Namen zu übertragen, überträgt die Datenbank bei String-Aggregation nur eine einzige, bereits zusammengefasste Zeile pro Gruppe. Bei Reports mit vielen Gruppen und großen 1:n-Beziehungen kann dieser Unterschied den übertragenen Datenumfang um eine Größenordnung reduzieren.
Ein Nachteil sollte trotzdem nicht übersehen werden: String-Aggregation kann bei sehr großen Gruppen und ohne passende Indizes auf den beteiligten Join-Spalten zu einer spürbaren CPU-Last auf der Datenbank führen, weil die Zusammenfassung selbst Rechenzeit kostet. Für Reports mit Millionen Detailzeilen pro Gruppe lohnt sich deshalb ein Blick auf den Ausführungsplan, um sicherzustellen, dass die zugrunde liegenden Joins über Indizes laufen, bevor die eigentliche String-Aggregation überhaupt beginnt.
9. String-Aggregation im Systemvergleich
Die folgende Übersicht fasst die wichtigsten Unterschiede zwischen den vier gängigen Implementierungen von String-Aggregation zusammen, damit der Wechsel zwischen Datenbanksystemen keine Überraschungen bei Syntax oder Verhalten bereithält.
| System | Funktion | Sortierung | Längenlimit |
|---|---|---|---|
| MySQL | GROUP_CONCAT(... SEPARATOR ...) |
ORDER BY innerhalb der Funktion | group_concat_max_len, schneidet still ab |
| PostgreSQL | STRING_AGG(ausdruck, trennzeichen) |
ORDER BY innerhalb der Funktion | Praktisch keine Begrenzung |
| SQL Server | STRING_AGG(ausdruck, trennzeichen) |
WITHIN GROUP (ORDER BY ...) | Praktisch keine Begrenzung |
| Oracle | LISTAGG(ausdruck, trennzeichen) |
WITHIN GROUP (ORDER BY ...) | 4000 Byte, wirft Fehler ohne ON OVERFLOW |
Mironsoft
SQL-Reporting, Datenbankmigrationen und Query-Optimierung
Reports mit umständlichen Textzusammenfassungen?
Wir bringen String-Aggregation sauber in eure Reporting-Abfragen, mit deterministischer Reihenfolge, sicheren Längenlimits und portablem SQL für den Wechsel zwischen Datenbanksystemen.
Query-Review
Prüfung bestehender GROUP_CONCAT- und STRING_AGG-Abfragen auf Reihenfolge und Limits
Migration
Portable String-Aggregation beim Wechsel zwischen MySQL, PostgreSQL und Oracle
Performance
Analyse von Ausführungsplänen bei großen 1:n-Aggregationen
Wer String-Aggregation von Anfang an mit explizitem ORDER BY und bewusst gesetzten Längenlimits einsetzt, vermeidet die typischen Überraschungen, die erst bei wachsenden Datenmengen sichtbar werden, etwa still abgeschnittene Ergebnisse oder eine plötzlich andere Reihenfolge nach einem Datenbank-Update.
10. Zusammenfassung
String-Aggregation fasst mehrere Zeilen einer Gruppe zu einer einzigen Textliste zusammen und ersetzt damit umständliche Zusammenführungen in der Anwendungsschicht. MySQL nutzt dafür GROUP_CONCAT mit SEPARATOR, PostgreSQL und SQL Server STRING_AGG mit einem zwingenden Trennzeichen-Parameter, Oracle LISTAGG mit WITHIN GROUP zur Sortierung. Alle vier Varianten lösen dasselbe Grundproblem, unterscheiden sich aber in Syntax, Reihenfolge-Garantien und Längenlimits.
Wer String-Aggregation produktiv einsetzt, sollte immer ein explizites ORDER BY innerhalb der Funktion setzen, Längenlimits wie group_concat_max_len bewusst konfigurieren und bei portablem SQL die systemspezifischen Unterschiede in der Dokumentation gegenprüfen. Richtig eingesetzt reduziert String-Aggregation sowohl die Komplexität der Anwendungsschicht als auch den übertragenen Datenumfang spürbar.
String-Aggregation: das Wichtigste auf einen Blick
Funktionsnamen
GROUP_CONCAT in MySQL, STRING_AGG in PostgreSQL und SQL Server, LISTAGG in Oracle.
Reihenfolge sichern
Immer ein explizites ORDER BY oder WITHIN GROUP setzen, sonst ist die Reihenfolge nicht garantiert.
Längenlimits beachten
group_concat_max_len in MySQL erhöhen, ON OVERFLOW in Oracle nutzen, um Fehler kontrolliert abzufangen.
Performance
Zusammenfassung in der Datenbank spart Netzwerk-Overhead gegenüber zeilenweiser Übertragung.