GROUP_CONCAT([DISTINCT] expr [,expr ...]
[ORDER BY {unsigned_integer | col_name | expr} [ASC | DESC] [,col_name ...]]
[SEPARATOR str_val])
| 參數 | 作用 | 默認值 |
|---|---|---|
DISTINCT | 去重 | 不去重 |
ORDER BY | 組內排序 | 無序 (依存儲順序) |
SEPARATOR | 分隔符 | 逗號 , |
-- 表結構
CREATE TABLE student_courses (
student_id INT UNSIGNED NOT NULL,
course_id INT UNSIGNED NOT NULL,
KEY(student_id)
);
-- 數據
INSERT INTO student_courses VALUES (1,1),(1,2),(2,3),(2,4),(2,5);
-- 查詢:學生 2 的課程列表
SELECT student_id, GROUP_CONCAT(course_id) AS courses
FROM student_courses
WHERE student_id = 2
GROUP BY student_id;
-- 結果: 2 | 3,4,5
SELECT student_id,
GROUP_CONCAT(course_id ORDER BY course_id DESC SEPARATOR '|||') AS courses
FROM student_courses
WHERE student_id = 2
GROUP BY student_id;
-- 結果: 2 | 5|||4|||3
$stmt = $pdo->query("SELECT student_id, GROUP_CONCAT(course_id) AS courses FROM student_courses WHERE student_id=2 GROUP BY student_id");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$course_ids = explode(',', $row['courses']); // [3, 4, 5]
-- 自定義分隔符
$course_ids = explode('|||', $row['courses']);
現象:GROUP_CONCAT(int_col) 在應用層收到的是 byte[] 而非字符串,SQL 工具 (Navicat, Workbench) 顯示正常,但程式語言驅動 (JDBC, PDO, mysqli) 返回二進制。
原因:MySQL 將數值類型拼接視為二進制數據流。
解決:強制轉換為字符類型
-- 方案 1: CAST (推薦,標準 SQL)
SELECT GROUP_CONCAT(CAST(id AS CHAR)) FROM large_table;
-- 方案 2: CONVERT
SELECT GROUP_CONCAT(CONVERT(id, CHAR)) FROM large_table;
-- 方案 3: CONCAT 隱式轉換 (僅單列)
SELECT GROUP_CONCAT(CONCAT(id, '')) FROM large_table;
默認限制:1024 字節 (1KB),超出部分靜默截斷,無報錯。
檢測:
SHOW VARIABLES LIKE 'group_concat_max_len';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| group_concat_max_len | 1024 |
+----------------------+-------+
修改 (會話級/全局級):
-- 當前會話 (推薦,不影響其他連接)
SET SESSION group_concat_max_len = 102400; -- 100KB
-- 全局永久 (需 my.cnf 配合)
SET GLOBAL group_concat_max_len = 102400;
-- my.cnf: [mysqld] group_concat_max_len = 102400
最大值:受限於 max_allowed_packet (默認 4MB/64MB),不可超過該值。
GROUP BY 列 + ORDER BY 列建立複合索引,避免臨時表 + 文件排序:
-- 查詢: GROUP BY student_id ORDER BY course_id
ALTER TABLE student_courses ADD INDEX idx_student_course (student_id, course_id);
無索引的 GROUP BY 會觸發 Using temporary; Using filesort,百萬行數據極慢。
GROUP_CONCAT 結果不可直接 LIMIT 控制組數,需子查詢:
SELECT student_id, courses FROM (
SELECT student_id, GROUP_CONCAT(course_id) AS courses
FROM student_courses
GROUP BY student_id
ORDER BY student_id
LIMIT 10 OFFSET 20
) t;
若單組拼接超過 max_allowed_packet:
JSON_ARRAYAGG() (MySQL 5.7+) 返回 JSON 數組,應用層解析| 特性 | GROUP_CONCAT | JSON_ARRAYAGG (5.7+) |
|---|---|---|
| 返回類型 | 字符串 (TEXT/BLOB) | JSON 文檔 |
| 類型保持 | 需手動 CAST | 自動保持數值/布爾/NULL |
| 嵌套結構 | 不支持 | 支持嵌套對象/數組 |
| 長度限制 | group_concat_max_len | max_allowed_packet |
| 解析便利性 | explode/split | 原生 JSON 解析器 |
GROUP_CONCAT是「將行轉列」的利器,但其長度限制、類型陷阱、性能敏感性要求開發者精通底層機制。現代應用建議優先評估JSON_ARRAYAGG,獲得更強的類型安全與結構表達能力。無論選擇哪種,索引設計、長度預估、應用層解析三板斧缺一不可。