PHP使用sqlsrv调用MSSQL存储过程无法获取返回状态与结果报错排查
问题描述
我查阅过多个标题看起来与本问题一致的相关提问,但我的实际场景存在差异,尝试了所有可行方案仍无法得到预期结果,以下是我已尝试内容的简要说明:
我的MSSQL表结构
Use Testing Go If Exists (Select * From sys.objects Where object_id=OBJECT_ID('dbo.Test_Table') And type in ('U')) Drop Table dbo.Test_Table Go Create Table dbo.Test_Table( FirstName NVarchar(50) Not Null Default '', LastName NVarchar(50) Not Null Default '', Age Int Not Null Default 0, Constraint PK_Test_Table Primary Key Clustered (FirstName) With (Pad_Index=Off,Statistics_NoRecompute=Off,Ignore_Dup_Key=Off,Allow_Row_Locks=On,Allow_Page_Locks=On) )
我的存储过程
Use Testing Go Set Ansi_Nulls On Go Set Quoted_Identifier On Go If Exists (Select * From sys.objects Where object_id=OBJECT_ID('dbo.SP_Test_Data') And type In ('P','PC')) Drop Procedure dbo.SP_Test_Data; Go Create Procedure dbo.SP_Test_Data @FirstName NVarchar(50) Out,@LastName NVarchar(50) Out,@Age Int Out As Begin Declare @status Int = 0; Begin Transaction Begin Try Insert Into Test_Table(FirstName,LastName,Age) Values (@FirstName,@LastName,@Age) If(@@Trancount > 0) Begin Commit Transaction; Set @status=1; End; End Try Begin Catch If @@Trancount > 0 Begin RollBack Transaction; Set @status=0; End; End Catch Select Stat=@status,FirstName=@FirstName,LastName=@LastName,Age=@Age Return @status; End; Go
MSSQL Management Studio测试情况
Use Testing Go Declare @FirstName NVarchar(50) = 'John',@LastName NVarchar(50) = 'Doe',@Age Int = 43,@stat Int = 0; Exec @stat = dbo.SP_Test_Data @FirstName Out,@LastName Out,@Age Out
该测试运行完全正常,插入成功时返回@stat=1,插入失败时返回@stat=0。
我的PHP代码
<?php $ServerName = '.\MSSQL2008'; $DBaseName = 'Testing'; $Output = array(); $firstName = 'John'; $lastName = 'Doe'; $age = 43; $sp_params = array( array($firstName, SQLSRV_PARAM_INOUT), array($lastName, SQLSRV_PARAM_INOUT), array($age, SQLSRV_PARAM_INOUT) ); try{ $Scon = sqlsrv_connect($ServerName); $RQS = "Exec ".$DBaseName.".dbo.SP_Test_Data @firstName=?,@lastName=?,@age=?"; $RST = sqlsrv_prepare($Scon, $RQS, $sp_params); if(!sqlsrv_execute($RST)){ $Output = array('status'=>'failed','type'=>'sql error','debug'=>$RQS,'result'=>$RST); } else{ if($row = sqlsrv_fetch_array($RST)){ $Output = $row; } } } catch(Exception $e){$e->getMessage();} if(($errors = sqlsrv_errors())!= null){ $Output = array('status'=>'failed','detail'=>$errors); } sqlsrv_close($ServerName); if(!empty($Output)){print_r($Output);} ?>
报错信息
无论存储过程执行成功还是失败,PHP侧都返回相同的报错:
Array ( [status] => failed [detail] => Array ( [0] => Array ( [0] => IMSSP [SQLSTATE] => IMSSP [1] => -28 [code] => -28 [2] => The active result for the query contains no fields. [message] => The active result for the query contains no fields. ) ) )
预期结果
希望能获取到存储过程的执行结果:执行成功返回1,失败返回0,同时希望能获取到如FirstName、Age等1-2个字段的值做校验。
解决方案
你的问题由多个错误点共同导致,逐个修复即可:
- 核心报错原因:存储过程中执行INSERT语句时,MSSQL默认会先返回一个仅包含受影响行数的结果集,这个结果集没有任何字段,你直接调用
sqlsrv_fetch_array读取的就是这个空结果集,所以触发报错。- 最优修复方案:在存储过程的开头(
Declare @status Int = 0;之前)添加SET NOCOUNT ON;,禁止存储过程返回DML操作的行计数结果,直接返回你最后SELECT的业务结果集。 - 备选修复方案:如果不想修改存储过程,可以在PHP执行
sqlsrv_execute之后,先调用sqlsrv_next_result($RST)跳到第二个业务结果集,再执行fetch操作。
- 最优修复方案:在存储过程的开头(
- 参数绑定错误:SQLSRV驱动要求绑定IN/OUT类型的参数时,必须显式指定参数的长度和PHP类型,否则无法正确处理输出值,参数绑定代码修改为:
$sp_params = array( array($firstName, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_UTF8), 50), array($lastName, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_UTF8), 50), array($age, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_INT) ); - 数据库连接配置缺失:
sqlsrv_connect仅传服务器名无法正常连接到指定数据库,需要补充连接配置:
同时要增加连接失败的判断,避免后续操作调用空资源报错。$connectionInfo = array( "Database" => $DBaseName, "UID" => "你的数据库登录账号", "PWD" => "你的数据库登录密码" ); $Scon = sqlsrv_connect($ServerName, $connectionInfo); - 存储过程调用语句不规范:调用带OUTPUT参数的存储过程时,需要在参数后加OUTPUT标识,SQL语句修改为:
$RQS = "Exec ".$DBaseName.".dbo.SP_Test_Data @firstName=? OUTPUT,@lastName=? OUTPUT,@age=? OUTPUT"; - 连接关闭参数错误:
sqlsrv_close需要传入连接资源而非服务器名字符串,修改为:sqlsrv_close($Scon);
所有错误修复后即可正常获取存储过程返回的status及相关字段值。
内容的提问来源于stack exchange,提问作者RKS
相关产品推荐
相关产品推荐

