PHP 透過 mssql(舊)或 sqlsrv/PDO_SQLSRV(現代)擴展調用 SQL Server 存儲過程時,處理輸出參數與返回值是常見痛點。本文以實戰代碼演示完整調用流程,並給出現代化遷移建議。
<?php
$hostname = 'tszzp';
$dbuser = 'sa';
$dbpasswd = '';
$dbname = 'stock';
$conn = mssql_connect($hostname, $dbuser, $dbpasswd) or die('無法連接服務器');
mssql_select_db($dbname, $conn) or die('無法選擇資料庫');
$stockcode = '600600';
// 構建 T-SQL 批次:宣告變數、執行存儲過程、選取輸出
$query = "DECLARE @result INT, @id INT, @msg VARCHAR(20);
EXEC @result = sp_test '$stockcode', @id OUTPUT, @msg OUTPUT;
SELECT @result AS result, @id AS id, @msg AS msg;";
$rs = mssql_query($query, $conn) or die('SQL 執行失敗: ' . $query);
$row = mssql_fetch_assoc($rs);
echo "Result: {$row['result']}<br>";
echo "ID: {$row['id']}, Msg: {$row['msg']}<br>";
mssql_free_result($rs);
mssql_close($conn);
?>
關鍵點:
DECLARE ... EXEC @return = proc ... OUTPUT ... SELECT @vars 批次包裝。OUTPUT,並在後續 SELECT 取回。mssql_fetch_assoc 直接以欄位名取值,避免數字索引易誤。強烈建議新專案使用 PDO_SQLSRV,支援預處理、參數綁定、異常處理,代碼更安全優雅。
<?php
$serverName = 'tszzp';
$connStr = "sqlsrv:Server=$serverName;Database=stock";
$user = 'sa'; $pass = '';
try {
$pdo = new PDO($connStr, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$stockcode = '600600';
$id = 0; $msg = '';
// 預處理語句:? 佔位符對應參數順序
$stmt = $pdo->prepare('EXEC sp_test @code = ?, @id = ?, @msg = ?');
// 綁定輸出參數:PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT
$stmt->bindParam(1, $stockcode, PDO::PARAM_STR);
$stmt->bindParam(2, $id, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, 20);
$stmt->bindParam(3, $msg, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 20);
$stmt->execute();
// 取得返回值(SQL Server 存儲過程 RETURN 值)
$returnValue = $stmt->fetchColumn(); // 若存儲過程有 SELECT 結果集,需先處理結果集
// 更穩健:使用 SQLSRV 驅動專屬函數 sqlsrv_next_result() 切換結果集
echo "Return: $returnValue<br>";
echo "Output ID: $id, Msg: $msg<br>";
} catch (PDOException $e) {
die('錯誤: ' . $e->getMessage());
}
?>
CREATE PROCEDURE sp_test
@code VARCHAR(20),
@id INT OUTPUT,
@msg VARCHAR(20) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
-- 業務邏輯...
SET @id = 12345;
SET @msg = 'SUCCESS';
RETURN 0; -- 返回值
END
| 坑點 | 解決方案 |
|---|---|
| 多結果集導致輸出參數讀不到 | PDO: $stmt->nextRowset() 切換;sqlsrv: sqlsrv_next_result() |
| OUTPUT 參數未指定長度導致截斷 | bindParam 第 4 參數指定最大長度 |
| 舊 mssql 擴展 PHP 7+ 已移除 | 強制遷移至 sqlsrv / PDO_SQLSRV |
| 字符集亂碼 | 連接字串加 CharacterSet=UTF-8,資料庫欄位用 NVARCHAR |
PHP 調用 SQL Server 存儲過程的核心在於:批次包裝輸出參數(舊版)或預處理綁定 INPUT_OUTPUT 參數(現代版)。新專務必使用 PDO_SQLSRV,配合嚴格錯誤模式與參數化查詢,徹底解決注入、編碼、資源洩漏等歷史問題。