PHP PDO Query在使用bindParam时返回一个空数组

时间:2014-10-14 00:02:26

标签: php sql-server pdo

有点被这个难倒。我已经开发了大约一个星期了,可能是两个,所以它可能是一个noob错误,但这就是我所拥有的:

<?php

$msDB = new PDO('odbc:Driver={SQL Server Native Client 11.0};Server=SOMESERVER;Trusted_Connection=yes;');
try{

//set values for query
$s="4";
$d1="'2014-10-01 00:00:00'";
$d2="'2014-10-31 23:59:59'";

//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN 
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID) 
AND (t1.SELECTION = :s) 
AND (tN.RESPONSE_DATE BETWEEN :d1 AND :d2))";

//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->bindParam(':s',$s, PDO::PARAM_INT);
$tbe->bindParam(':d1',$d1, PDO::PARAM_STR);
$tbe->bindParam(':d2',$d2, PDO::PARAM_STR);

//execute query
$tbe->execute();

//fetch resulting data
$res = $tbe->fetchAll(PDO::FETCH_ASSOC);}

//error handling
catch (PDOException $e) {
    throw new pdoDbException($e);
}
//print the resulting array
print_r($res);

//set initial count
$cnt=0;

//loop through and increment count
foreach($res as $key=>$value){
    foreach($value as $v2 ){
        $cnt++;
    }
}

//return count value
echo "Total:<br/>".$cnt."<br/>";

?>

我希望这会返回数字3的结果集。当我手动指定查询中的值时,一切都按预期工作,并返回数字3.

但是,如果我使用bindParam方法,它返回任何内容并且不会抛出任何类型的错误。它只返回一个空数组。

我还可以分解$ q1中的查询集并将值连接到它中,它也可以完美地运行。我以前没有真正使用过bindParam,但就我所知,我正确使用它。

使用:

//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (t1.SELECTION = ".$s.")
AND (tN.RESPONSE_DATE BETWEEN ".$d1." AND ".$d2."))";

当我在MSSQL Server Management Studio中运行查询时,它还会返回我期望的结果集。

谁能告诉我我做错了什么?

2 个答案:

答案 0 :(得分:1)

PDO :: bindParam和PDO :: bindValue传递的默认数据类型是始终文本。从MSSQL中的数据类型文本转换只能对CHAR,VARCHAR,NCHAR和NVARCHAR进行转换。由于数据类型问题,必须将值从文本转换为CHAR或VARCHAR,然后从那里转换为DATETIME。但这是一个隐式转换,根据传递给查询的值,可能会导致舍入错误,截断或转换失败。

这是 NOT 工作:

//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN 
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID) 
AND (tN.RESPONSE_DATE BETWEEN :d1 AND :d2))";

//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->bindParam(':d1',$d1, PDO::PARAM_INT);
$tbe->bindParam(':d2',$d2, PDO::PARAM_INT);

确实有效:

$q1 = 'SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
    (SELECT * FROM SURVEY_RESPONSE AS tN
    WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
    AND (tN.RESPONSE_DATE BETWEEN CONVERT(datetime,CONVERT(VARCHAR(MAX),?)) 
    AND CONVERT(datetime,CONVERT(VARCHAR(MAX),?))))';

//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$tbe->bindParam(1,$d1);
$tbe->bindParam(2,$d2);

即使我将绑定参数作为类型INT传输,它们仍然作为文本传递给MSSQL导致失败。

然而,我的建议是简单地使用原始的解决方法直接传递变量,并将变量设为双引号字符串:

$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
    (SELECT * FROM SURVEY_RESPONSE AS tN
    WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
    AND (t1.SELECTION = '$s')
    AND (tN.RESPONSE_DATE BETWEEN '$date1' AND '$date2'))";

引用的字符串更容易处理,并且更不容易出错,因为它根本不需要转换,只是简单地传递给MSSQL Server而没有问题。

由于MSSQL Server的额外处理,我注意到转换器的性能略有下降。使用转换运行时,查询花费的时间比没有查询长约半秒。

欢迎@meda帮助解决问题!

答案 1 :(得分:0)

我今天得到了这个错误并花了很多时间。 将PHP版本升级到PHP7后,使用sqlsrv:database驱动程序而不是dblib:dbname会出现此问题。 为了避免这种情况,仍然使用 dblib:dbname 事件PHP7 FPM已经支持sqlsrv:数据库驱动程序。