如何使用 sqlsrv 和“?”在 php 中执行存储过程样式参数

How to execute a stored procedure in php using sqlsrv and "?" style parameters

我查看了其他几个看起来(从标题上看)与此相同的问题。但是,我的情况有点不同。

以下工作(即我得到 "success" 并且当 运行 使用给定变量的过程时我的数据库执行我期望的):

$sql = "MyDB.dbo.myProcedure {$var1}, {$var2}, {$var3}";
$result = sqlsrv_query($myConn, $sql);
if (!$result) {
    echo 'Your code is fail.';
}
else {
    echo 'Success!';
}

我想通过使用参数创建 SQL 字符串来避免(或减少)SQL 注入。例如:

$sql = "select * from aTable where col1 = ? AND col2 = ?";
$result = sqlsrv_query($myConn, $sql, array($var1, $var2));
//please note. This code WILL work!

但是当我使用存储过程执行此操作时,它失败了。它失败了,没有通过 sqlsrv_errors() 报告错误,没有在数据库中采取任何行动,并且 $result === false.

要清楚,以下失败:

$sql = "MyDB.dbo.myProcedure ?, ?, ?";
$result = sqlsrv_query($myConn, $sql, array($var1, $var2, $var3));

同样,以相同方式创建的 prepare/execute 语句也会失败:

$sql = "MyDB.dbo.myProcedure ?, ?, ?";
$stmt = sqlsrv_prepare($myConn, $sql, array(&$var1, &$var2, &$var3));
foreach($someArray as $key => $var3) {
    if(sqlsrv_execute($stmt) === false) {
        echo 'mucho fail.';
    }
}
//this code also fails.

为了完整起见,我已经确认有问题的存储过程直接在 SQL Management Studio 中工作,并且如果按照我上面提到的方式调用的话。同样,我已经确认我 可以 对任何原始查询使用参数化查询(例如插入、select、更新与存储过程)。

所以,我的问题是如何使用参数化查询调用存储过程与在查询字符串中嵌入变量?

更重要的是,我实际上想使用 prepare/execute,所以希望答案也能让它起作用。

php.net 上的用户贡献了一篇关于如何使用 sqlsrv-prepare 执行存储过程的文章。

如果将来从 php.net 用户贡献中删除,这里是它列出的内容:

$procedure_params = array(
array(&$myparams['Item_ID'], SQLSRV_PARAM_OUT),
array(&$myparams['Item_Name'], SQLSRV_PARAM_OUT)
);
// EXEC the procedure, {call stp_Create_Item (@Item_ID = ?, @Item_Name = ?)} seems to fail with various errors in my experiments
$sql = "EXEC stp_Create_Item @Item_ID = ?, @Item_Name = ?";
$stmt = sqlsrv_prepare($conn, $sql, $procedure_params);

这是手册页,http://php.net/manual/en/function.sqlsrv-prepare.php

这是对@chris85 回答的跟进。

这里值得注意的是,准备好语句后,您需要执行它:

$sql = "EXEC stp_Create_Item @Item_ID = ?, @Item_Name = ?";
$stmt = sqlsrv_prepare($conn, $sql, $procedure_params);
if (!sqlsrv_execute($stmt)) {
    echo "Your code is fail!";
    die;
}
while($row = sqlsrv_fetch_array($stmt)){
    //Stuff
}

sqlsrv_execute() 仅 returns true/false。如果你想解析存储过程返回的数据,你可以像 sqlsrv_query().

的结果一样处理它

如果您忘记了 sqlsrv_execute(),您会收到一条错误消息,提示必须先执行结果才能使用。

确保设置此项,否则如果存储过程返回消息,您将始终返回错误。

sqlsrv_configure('WarningsReturnAsErrors',0);

//Full working code below

$sql = "{call NameOfDatabase.NameOfOwner.StoredProcedureName(?,?)}";

$params = array($param1, $param2); 

if ($stmt = sqlsrv_prepare($conn, $sql, $params)) {
    echo "Statement prepared.<br><br>\n";  

} else {  
    echo "Statement could not be prepared.\n";  
    die(print_r(sqlsrv_errors(), true));  
} 

if( sqlsrv_execute( $stmt ) === false ) {

    die( print_r( sqlsrv_errors(), true));

}else{

    print_r(sqlsrv_fetch_array($stmt));

}

这是@chris85 回答的另一个后续。

我尝试了这个答案,并结合@AndyD273 的后续答案,但得到了以下错误:I get the exception "The formal parameter "@param1" was not declared as an OUTPUT parameter, but the actual parameter passed in requested output"

我通过将 SQLSRV_PARAM_OUT 的所有实例更改为 SQLSRV_PARAM_IN 解决了这个问题。 SQLSRV_PARAM_INdocumentation 表示:

Indicates an input parameter when passed to sqlsrv_query() or sqlsrv_prepare().

@chris85 准备存储过程参数的答案的更新版本现在看起来像:

$procedure_params = array(
    array(&$myparams['Item_ID'], SQLSRV_PARAM_IN),
    array(&$myparams['Item_Name'], SQLSRV_PARAM_IN)
);