PHP PDO调用SQL Server存储过程:为何需2次nextRowset()?行集含什么?
问题描述
我正在用PHP PDO向SQL Server存储过程插入数据,需要获取受影响行数、返回值和输出参数,目前代码能正常运行。
连接代码:
$conn = new PDO("sqlsrv:Server=$hostname;Database=$dbname", "$username", "$pw"); echo "Connection established.<br />"; $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);; $conn->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM);
调用存储过程的代码:
$sql = "EXEC ? = the_stored_procedure ?, ?"; $sth = $conn->prepare($sql); $retval = 99; $bite = '999'; $sth->bindParam(1, $retval, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE); $sth->bindParam(2, $bite); $sth->bindParam(3, $responseMessage, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 512); $sth->execute();
获取结果的代码:
echo "rowcount: " . $sth->rowCount() . "<br>"; $sth->nextRowset(); $sth->nextRowset(); echo "RETURN: " . $retval . "<br>"; echo "OUTPUT: " . $responseMessage . "<br>";
我的问题是:为什么需要调用2次nextRowset()?这两个行集中包含什么内容?
另外,如果我把EXEC语句改成:
$sql = "SET NOCOUNT ON; EXEC ? = ComC_i_Survey_Start_php ?, ?";
就无法获取行计数,但不用调用nextRowset()就能直接拿到返回值和输出参数:
echo "<br>RETURN:" . $retval . "<br>"; echo "<br>OUTPUT:" . $responseMessage . "<br>";
回答
为什么需要两次nextRowset()?
这是SQL Server和PDO SQLSRV驱动的特性决定的:当你不添加SET NOCOUNT ON时,执行带返回值的存储过程会生成两个额外的行集,必须遍历完这些行集,PDO才能将存储过程的返回值和输出参数填充到你绑定的变量中。
两个行集的具体内容:
- 第一个行集:存储过程中DML操作(比如你代码里的INSERT)产生的受影响行数消息。SQL Server默认会为每个DML操作返回这个结果集,这也是你能通过
$sth->rowCount()拿到行数的原因。 - 第二个行集:存储过程的返回值结果集。因为你用了
EXEC ? = the_stored_procedure的语法来捕获存储过程的返回值,SQL Server会把这个返回值包装成一个单独的行集返回。
只有调用两次nextRowset()跳过这两个行集后,PDO才会同步更新你绑定的$retval(返回值)和$responseMessage(输出参数)变量的值。
关于SET NOCOUNT ON的情况
SET NOCOUNT ON会抑制SQL Server返回DML操作的受影响行数消息,也就是去掉了第一个行集。此时执行存储过程不会生成额外的行集(如果存储过程内部没有SELECT语句的话),所以PDO不需要遍历行集就能直接访问绑定的输出参数——但代价是你再也无法通过rowCount()获取受影响的行数了,因为这个信息被强制抑制。
内容的提问来源于stack exchange,提问作者user9124752
相关产品推荐
相关产品推荐

