在企業級應用中,PHP 透過 PDO 操作 SQL Server 預存程序是常見需求。但「單次呼叫取得多個結果集」且「同時接收 OUTPUT 參數」的正確姿勢,官方文檔較少示範。本文提供經實測可用的完整程式碼與關鍵細節說明。
CREATE PROCEDURE [dbo].[P_Test_GetMixData](
@Message_1 tinyint OUTPUT,
@Message_2 varchar(10) OUTPUT,
@SearchValue varchar(50)
) AS
SET NOCOUNT ON
SET @Message_1 = 123
SET @Message_2 = 'Hi,there!'
SELECT * FROM _T1 WHERE col1 LIKE '%' + @SearchValue + '%'
SELECT * FROM _T2 WHERE col1 LIKE '%' + @SearchValue + '%'
SET NOCOUNT OFF
關鍵點: SET NOCOUNT ON 抑制「影響 n 筆資料」訊息,避免干擾結果集解析;兩個 SELECT 產生兩個結果集;兩個 OUTPUT 參數在結束時自動回傳。
$dbh = new PDO('sqlsrv:server=主機;Database=資料庫', '帳號', '密碼');
try {
$procName = 'P_Test_GetMixData';
$stmt = $dbh->prepare("EXEC $procName ?, ?, ?");
// 參數綁定:OUTPUT 參數必須指定長度或使用預設大小常數
$nReturnValue = 0;
$strReturnValue = '';
$strSearchValue = 'abandonship';
$stmt->bindParam(1, $nReturnValue, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE);
$stmt->bindParam(2, $strReturnValue, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 10);
$stmt->bindParam(3, $strSearchValue, PDO::PARAM_STR);
$stmt->execute();
// --- 結果集 1 ---
$rowset_1 = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($rowset_1);
echo '
';
// --- 結果集 2 ---
$stmt->nextRowset(); // 移至下一個結果集
$rowset_2 = $stmt->fetch();
print_r($rowset_2);
echo '
';
// --- 取得 OUTPUT 參數值(必須在所有結果集處理完後) ---
$stmt->nextRowset(); // 游標推進至結束
echo $nReturnValue . '
';
echo $strReturnValue;
} catch (Exception $e) {
echo $e->getMessage();
}
PDO::PARAM_INPUT_OUTPUT 時,第 4 參數不可省略。PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE 適用於 PARAM_INT/PARAM_BOOL;字串類型需給具體長度(如 10)。nextRowset() 將游標推進下一個結果集,不可跳過。nextRowset() 後,變數 $nReturnValue、$strReturnValue 才會被驅動程式填入實際值。SET NOCOUNT ON 是好習慣:否則「n rows affected」會被視為額外結果集,導致 nextRowset() 次數對不上。bindParam 報錯 SQLSTATE[IMSSP]: The active result for the query contains no fields,通常是 nextRowset() 呼叫次數過多或未處理完所有結果集就嘗試讀取 OUTPUT。PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE 常數(需較新版本)。