Grouping & aggregation

Concatenate rows into one string per group

3 min · updated June 16, 2026

“All products per customer, comma-separated.” Every engine can do it; the function name differs.

-- PostgreSQL, BigQuery, DuckDB
SELECT customer_id, STRING_AGG(product, ', ' ORDER BY product) AS products
FROM orders GROUP BY customer_id;

-- MySQL
SELECT customer_id, GROUP_CONCAT(product ORDER BY product SEPARATOR ', ') AS products
FROM orders GROUP BY customer_id;

-- SQLite  (separator is the 2nd arg; ORDER BY needs 3.44+)
SELECT customer_id, GROUP_CONCAT(product, ', ') AS products
FROM orders GROUP BY customer_id;

-- Snowflake
SELECT customer_id, LISTAGG(product, ', ') WITHIN GROUP (ORDER BY product) AS products
FROM orders GROUP BY customer_id;

Two traps.

  1. MySQL silently truncates at group_concat_max_len (default 1024 bytes). Raise it for long lists: SET SESSION group_concat_max_len = 1000000;.
  2. Ordering isn’t guaranteed unless you ask. Put ORDER BY inside the aggregate (as shown). SQLite’s GROUP_CONCAT only accepts an ORDER BY argument from version 3.44; on older SQLite, order in a subquery first.

Distinct values only:

SELECT STRING_AGG(DISTINCT product, ', ') FROM orders;     -- Postgres / DuckDB
SELECT GROUP_CONCAT(DISTINCT product)     FROM orders;     -- MySQL / SQLite

← All recipes