以「學生-課程-成績」三表為例,展示如何用 GROUP_CONCAT 動態生成列、配合 PREPARE/EXECUTE 實現動態行轉列,並封裝為存儲過程支援條件參數,解決 MyBatis 等 ORM 無法直接執行動態 SQL 的難題。
student(stuid, stunm)courses(courseno, coursenm)score(stuid, courseno, scores)SELECT st.stuid, st.stunm, MAX(CASE c.coursenm WHEN '大學語文' THEN s.scores ELSE 0 END) AS '大學語文', MAX(CASE c.coursenm WHEN '線性代數' THEN IFNULL(s.scores,0) ELSE 0 END) AS '線性代數', ... FROM student st LEFT JOIN score s ON st.stuid = s.stuid LEFT JOIN courses c ON c.courseno = s.courseno GROUP BY st.stuid;
SELECT GROUP_CONCAT(DISTINCT CONCAT( 'MAX(IF(c.coursenm = ''', c.coursenm, ''', s.scores, 0)) AS ''', c.coursenm, '''' )) FROM courses c;
輸出:MAX(IF(c.coursenm = '大學語文', s.scores, 0)) AS '大學語文', MAX(IF(c.coursenm = '線性代數', s.scores, 0)) AS '線性代數', ...
SET @sql = NULL;
SELECT GROUP_CONCAT(...) INTO @sql FROM courses c;
SET @sql = CONCAT('SELECT st.stuid, st.stunm, ', @sql,
' FROM student st LEFT JOIN score s ON st.stuid = s.stuid LEFT JOIN courses c ON c.courseno = s.courseno GROUP BY st.stuid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DELIMITER $$
DROP PROCEDURE IF EXISTS SP_QueryData;
CREATE PROCEDURE SP_QueryData(IN p_stuid VARCHAR(16))
READS SQL DATA
BEGIN
SET @sql = NULL;
SET @stuid = NULL;
SELECT GROUP_CONCAT(DISTINCT CONCAT(
'MAX(IF(c.coursenm = ''', c.coursenm, ''', s.scores, 0)) AS ''', c.coursenm, ''''
)) INTO @sql FROM courses c;
SET @sql = CONCAT('SELECT st.stuid, st.stunm, ', @sql,
' FROM student st LEFT JOIN score s ON st.stuid = s.stuid LEFT JOIN courses c ON c.courseno = s.courseno');
IF p_stuid IS NOT NULL AND p_stuid <> '' THEN
SET @stuid = p_stuid;
SET @sql = CONCAT(@sql, ' WHERE st.stuid = ''', @stuid, '''');
END IF;
SET @sql = CONCAT(@sql, ' GROUP BY st.stuid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
CALL SP_QueryData('1001'); -- 單學生
CALL SP_QueryData(''); -- 全部學生
IF condition THEN ... END IF; 無括號、無大括號。@sql 用戶變量跨語句傳遞,p_stuid 為輸入參數。{call SP_QueryData(#{stuid})},避開 XML 動態 SQL 限制。此模式適用於「列不固定、需動態生成、且需條件過濾」的報表查詢場景。