在一對多關係未建立中間表的情況下,常將「多」的一方用分隔符(如逗號)存儲在「一」的一方某字段中。雖然節省了關聯表,但導致無法直接用 SQL 進行關聯查詢,通常只能在應用層拆分後多次查詢。本文展示如何純 SQL 實現行轉列,將單行逗號分隔字符串拆分為多行記錄。
假設 user 表結構如下:
+----+------+------------------+
| id | name | mobile |
+----+------+------------------+
| 1 | 張三 | 13800001111,13900002222 |
+----+------+------------------+
期望查詢結果:
+------+--------------+
| name | mobile |
+------+--------------+
| 張三 | 13800001111 |
| 張三 | 13900002222 |
+------+--------------+
創建一張僅包含連續自增 ID 的序號表 sequence(或 squence),ID 範圍覆蓋單字段拆分後的最大可能數量。
CREATE TABLE sequence (id INT PRIMARY KEY AUTO_INCREMENT);
-- 插入足夠多的行,例如 10000 行
INSERT INTO sequence VALUES (), (), ... ;
SELECT
b.name,
REPLACE(
SUBSTRING_INDEX(b.mobile, ',', a.id),
CONCAT(SUBSTRING_INDEX(b.mobile, ',', a.id - 1), ','),
''
) AS mobile
FROM sequence a
CROSS JOIN (
SELECT
name,
CONCAT(mobile, ',') AS mobile, -- 統一結尾添加分隔符,便於 SUBSTRING_INDEX 邏輯
LENGTH(mobile) - LENGTH(REPLACE(mobile, ',', '')) + 1 AS size -- 計算分隔符數量+1 = 字段數
FROM `user`
) b
ON a.id <= b.size;
mobile 尾部拼接逗號,確保每個字段後都有分隔符;同時計算字段個數 size。CROSS JOIN 產生笛卡爾積,通過 ON a.id <= b.size 只保留有效拆分行數。SUBSTRING_INDEX(str, delim, N) 返回第 N 個分隔符之前的子串(含前 N-1 個字段)。WITH RECURSIVE 遞歸 CTE 或 JSON_TABLE 替代序號表,寫法更簡潔。