MySQL 自定義函數(FUNCTION)僅能返回單一標量值,需返回多個結果集時必須使用儲存過程。本文展示一個接受兩條 SQL、返回兩個結果集的儲存過程,並給出完整 Java (JDBC) 呼叫代碼。
DROP PROCEDURE IF EXISTS `testguocheng`;
CREATE PROCEDURE `testguocheng`(
IN `sql1` VARCHAR(100),
IN `sql2` VARCHAR(100)
)
BEGIN
-- 第一個結果集
SET @SQLStr1 = CONCAT(sql1);
PREPARE stmt1 FROM @SQLStr1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
-- 第二個結果集
SET @SQLStr2 = CONCAT(sql2);
PREPARE stmt2 FROM @SQLStr2;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
END;
EXECUTE 產生一個結果集,JDBC 依序讀取。DEALLOCATE PREPARE 釋放準備句柄。public static void getData() {
Connection conn = null;
CallableStatement stmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection(); // 自行實現的連線池工具
// 呼叫儲存過程,傳入兩條 SQL
String callSql = "{call testguocheng('SELECT * FROM tsys_user;', 'SELECT COUNT(*) FROM tsys_user;')}";
stmt = conn.prepareCall(callSql);
boolean hasResult = stmt.execute();
int resultSetIndex = 0;
while (hasResult) {
resultSetIndex++;
System.out.println("=== 第 " + resultSetIndex + " 個結果集 ===");
rs = stmt.getResultSet();
if (rs != null) {
if (resultSetIndex == 1) {
// 第一個結果集:用戶明細
while (rs.next()) {
System.out.println("uid: " + rs.getString("uid") +
", uname: " + rs.getString("uname"));
}
} else {
// 第二個結果集:聚合統計
while (rs.next()) {
System.out.println("總數:" + rs.getString(1));
}
}
rs.close();
}
// 移動到下一個結果集
hasResult = stmt.getMoreResults();
}
} catch (Exception e) {
System.err.println("執行異常: " + e.getMessage());
e.printStackTrace();
} finally {
// 資源釋放順序:ResultSet → Statement → Connection
try { if (rs != null) rs.close(); } catch (Exception ignore) {}
try { if (stmt != null) stmt.close(); } catch (Exception ignore) {}
try { if (conn != null) conn.close(); } catch (Exception ignore) {}
}
}
stmt.execute() 返回第一個結果集是否存在。stmt.getResultSet() 取得當前結果集。rs.close()。stmt.getMoreResults() 移至下一個結果集,返回 true 繼續迴圈。上述範例直接拼接 SQL 字串,存在注入風險。生產環境建議:
CREATE PROCEDURE `safe_proc`(IN tbl VARCHAR(64), IN min_id INT)
BEGIN
SET @sql = CONCAT('SELECT * FROM ', QUOTENAME(tbl), ' WHERE id > ?');
PREPARE stmt FROM @sql;
SET @p = min_id;
EXECUTE stmt USING @p;
DEALLOCATE PREPARE stmt;
END
Java 端呼叫:{call safe_proc('tsys_user', ?)} 並 stmt.setInt(1, 100)。
儲存過程 + PREPARE/EXECUTE 實現動態多結果集返回;JDBC 端以 getMoreResults() 迴圈消費。務必做好參數化、資源釋放與異常處理,構建健壯的資料存取層。