GROUP_CONCAT 是 MySQL 4.1 引入的聚合函數,能將同一群組內的多行欄位值串聯成單一字串,極大簡化「一對多」關係的資料呈現(如:文章標籤、用戶權限列表、訂單商品名稱)。
GROUP_CONCAT([DISTINCT] expr [ORDER BY ...] [SEPARATOR str])
DISTINCT:去除重複值。ORDER BY:指定串聯順序。SEPARATOR:自訂分隔符(預設為逗號 ,)。-- 建立測試表
CREATE TABLE `ta` (`id` INT, `name` VARCHAR(10));
INSERT INTO `ta` VALUES
(1,'a'),(1,'b'),(1,'c'),(1,'d'),
(2,'a'),(2,'b'),(2,'c'),
(3,'d');
-- 依 id 分組,將 name 串聯
SELECT `id`, GROUP_CONCAT(`name`) FROM `ta` GROUP BY `id`;
/* 結果:
id | GROUP_CONCAT(`name`)
---|---------------------
1 | a,b,c,d
2 | a,b,c
3 | d
*/
SELECT `id`,
GROUP_CONCAT(`name` ORDER BY `name` DESC SEPARATOR ' | ') AS names
FROM `ta` GROUP BY `id`;
/* id=1 → d | c | b | a */
SELECT `id`, GROUP_CONCAT(DISTINCT `name`) FROM `ta` GROUP BY `id`;
GROUP_CONCAT 最大長度受系統變數 group_concat_max_len 控制(預設 1024 位元組)。超截斷不報錯。可臨時調大:
SET SESSION group_concat_max_len = 1000000;
SELECT `id`, GROUP_CONCAT(`name`) FROM `ta` GROUP BY `id`;
SELECT `id`,
CONCAT('[', GROUP_CONCAT(CONCAT('"', `name`, '"')), ']') AS json_names
FROM `ta` GROUP BY `id`;
/* id=1 → ["a","b","c","d"] */
| 場景 | 範例 |
|---|---|
| 文章標籤顯示 | SELECT article_id, GROUP_CONCAT(tag_name) FROM article_tags GROUP BY article_id |
| 用戶角色權限 | SELECT user_id, GROUP_CONCAT(role_name) FROM user_roles GROUP BY user_id |
| 訂單商品清單 | SELECT order_id, GROUP_CONCAT(product_name SEPARATOR '; ') FROM order_items GROUP BY order_id |
| 報表彙總匯出 CSV | 直接在 SQL 產出單欄位多值字串,減少應用層迴圈 |
group_concat_max_len 與 max_allowed_packet。ORDER BY 在群組內排序會增加 CPU,必要時才加。GROUP_CONCAT 自動忽略 NULL 值。OVER (PARTITION BY ...),只能配合 GROUP BY。JSON_ARRAYAGG() 產出 JSON 陣列,結構更豐富、解析更方便。-- 錯誤:未加 GROUP BY 直接用 GROUP_CONCAT
SELECT GROUP_CONCAT(name) FROM ta; -- 若 ONLY_FULL_GROUP_BY 會報錯
-- 正確:搭配 GROUP BY
SELECT id, GROUP_CONCAT(name) FROM ta GROUP BY id;
善用 GROUP_CONCAT 能大幅減少應用程式的迴圈查詢與字串拼接邏輯,是「一對多」資料扁平化輸出的利器。