MySQL中的"行轉列"是一個經典且非常實用的數據處理技巧,通常用于將數據從一種易于存儲的格式轉換為一種更易于閱讀和報告的格式。下面我將詳細解釋其概念、方法和應用場景。
- **行數據**:通常指數據庫中最自然的存儲格式,每一行代表一條獨立的記錄。
| :------- | :--- | :--- |
- **列數據**(行轉列后的目標):將某一列的唯一值(如`科目`)轉換為新的列名,并將其對應的值(如`成績`)填充到新列下。
| :------- | :--- | :--- |
方法一:使用 `CASE WHEN` + 聚合函數(最通用、最靈活)
這是最經典和兼容性最好的方法,適用于幾乎所有版本的 MySQL。
1. 使用 `GROUP BY` 按唯一標識(如`學生姓名`)分組,確保一行一個學生。
2. 對每個需要轉換的列值,使用 `CASE WHEN`(或 `IF`)語句判斷并提取對應的值。
3. 使用 `MAX` 或 `MIN`、`SUM` 等聚合函數對提取出的值進行聚合(通常用于確保每個分組下只有一個非空值)。
MAX(CASE WHEN 科目 = '語文' THEN 成績 ELSE NULL END) AS 語文,
MAX(CASE WHEN 科目 = '數學' THEN 成績 ELSE NULL END) AS 數學
| :------- | :--- | :--- |
因為 `GROUP BY` 后,每個學生的每門科目理論上只有一條記錄。`MAX` 的作用是取回那條唯一記錄的值。如果不用聚合函數,SQL 會報錯。你也可以用 `MIN` 或 `SUM`,效果相同。
方法二:使用 `IF()` + 聚合函數(MySQL 的簡潔寫法)
`IF(condition, value_if_true, value_if_false)` 是 `CASE WHEN` 的簡化版,邏輯更清晰。
MAX(IF(科目 = '語文', 成績, NULL)) AS 語文,
MAX(IF(科目 = '數學', 成績, NULL)) AS 數學
方法三:使用 `GROUP_CONCAT`(另一種形式的“行轉列”)
有時“轉列”的目的不是生成多個新列,而是將多行數據**合并到一個單元格**中,用分隔符連接。這時 `GROUP_CONCAT` 非常有用。
GROUP_CONCAT(科目, ':', 成績 SEPARATOR '; ') AS 成績單
| :------- | :----------------- |
上面的方法有個巨大缺陷:**需要手動編寫每個要轉換的列值(如‘語文’、‘數學’)**。如果科目是不確定的(例如隨時會增加‘英語’、‘物理’),上面的 SQL 就無法滿足需求。
這時需要使用**存儲過程(Stored Procedure)** 來**動態**生成 SQL 語句。
1. 查詢出所有需要轉換為列名的唯一值(如所有科目)。
2. 使用字符串拼接函數(如 `CONCAT`, `GROUP_CONCAT`)構造出包含所有 `CASE WHEN` 語句的 SQL 字符串。
3. 使用預處理語句(`PREPARE` & `EXECUTE`)來執行這個動態生成的 SQL 字符串。
-- 2. 查詢所有不重復的科目,并拼接成 MAX(CASE WHEN...) 的格式
CONCAT('`', 科目, '`') -- 用反引號包裹科目名,防止它是關鍵字
SET @sql = CONCAT('SELECT 學生姓名, ', @sql, ' FROM scores GROUP BY 學生姓名');
**執行結果**:無論科目如何變化,這段代碼都會自動生成一個將所有科目作為列名的透視表。
| 學生姓名 | 語文 | 數學 | 英語 | (其他任何科目...) |
| :------- | :--- | :--- | :--- | :------------------ |
| 張三 | 90 | 85 | 92 | ... |
| 李四 | 95 | 70 | 88 | ... |
| :----------------- | :--------------------------------------------- | :--------------------------- | :----------------------------- |
| **`CASE WHEN`/`IF`** | 要轉換的列值是**固定的、已知的**(如科目固定) | 簡單、直觀、性能好 | 無法自動適應列值的變化 |
| **`GROUP_CONCAT`** | 需要將多行數據合并到一個字段中顯示 | 非常靈活,不會改變表結構 | 結果不是一個規整的二維表 |
| **動態SQL** | 要轉換的列值是**動態的、不確定的**(如科目可變) | 全自動,一勞永逸 | 編寫復雜,需要用到存儲過程和預處理 |
2. 靜態轉換使用 `CASE WHEN`/`IF` + `MAX` + `GROUP BY`。
3. 動態轉換需要使用存儲過程拼接 SQL,復雜度較高。
4. 在選擇方法前,一定要明確需求中的“列”是否是固定的。
另外搭配便捷的MYSQL備份工具,可定時備份、異地備份,MYSQL導出導入?杀镜剡B接LINUX里的MYSQL,簡單便捷。可以大大地提高工作效率喔。