部分问题指出:
我发现使用参数会生成带引号的因子
所以,我猜查询不一定是这样写的,但引号是由查询处理器自动添加的。即使查询处理器没有添加引号,所获得的输出也类似于在显式添加引号时会看到的行为。因此,在某种程度上,暗示获得的结果比 OP 的一些粗心发挥更隐含。但是,仍有待观察。如果没有可验证的例子,我不想激起辩论或进一步评论。所以,就这样吧……
方案一:
$servername = "localhost";
$username = "test";
$password = "test";
$dbname = "testdb";
$array = array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("SELECT ROUND(1.945,2) AS test_round,
ROUND((ROUND((7002) / ?) * ?) / 3600,2) AS param_rounded,
ROUND(
(7002 / ?)* ?
/ 3603,5
)AS param_rounded_5_places,
ROUND(
(7002 / ?)* ?
/ 3603,4
)AS param_rounded_4_places,
ROUND(
(7002 / ?)* ?
/ 3603,3
)AS param_rounded_3_places,
ROUND((ROUND((7002) / 1) * 1) / 3600,2) AS hard_rounded,
ROUND((ROUND((7002) / CAST(? AS DECIMAL(10,2))) * CAST(? AS DECIMAL(10,2))) / 3600,2) AS param_rounded_modified"
);
$stmt->bindParam(1, $array[0]);
$stmt->bindParam(2, $array[1]);
$stmt->bindParam(3, $array[2]);
$stmt->bindParam(4, $array[3]);
$stmt->bindParam(5, $array[4]);
$stmt->bindParam(6, $array[5]);
$stmt->bindParam(7, $array[6]);
$stmt->bindParam(8, $array[7]);
$stmt->bindParam(9, $array[8]);
$stmt->bindParam(10, $array[9]);
$stmt->execute();
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
}
catch(PDOException $e){
echo "Diagnostic: ".$e;
}
方案二:
这个程序与第一个程序完全相同,除了:
$stmt->bindParam(1, $array[0], PDO::PARAM_INT);
$stmt->bindParam(2, $array[1], PDO::PARAM_INT);
$stmt->bindParam(3, $array[2], PDO::PARAM_INT);
$stmt->bindParam(4, $array[3], PDO::PARAM_INT);
$stmt->bindParam(5, $array[4], PDO::PARAM_INT);
$stmt->bindParam(6, $array[5], PDO::PARAM_INT);
$stmt->bindParam(7, $array[6], PDO::PARAM_INT);
$stmt->bindParam(8, $array[7], PDO::PARAM_INT);
$stmt->bindParam(9, $array[8], PDO::PARAM_INT);
$stmt->bindParam(10, $array[9], PDO::PARAM_INT);
现在,大多数用户习惯于用前一种方式编写查询:
$stmt->bindParam(1, $array[0]);
这可能适用于大多数算术运算(至少在我个人到目前为止的经验中),但ROUND 似乎突出了一个可能的问题。不受欢迎的截断/四舍五入...
Computed Expected Obtained
1.945 1.95 1.94
Demo - 虽然它只是一个直接的 MySQL 查询,但其行为与 PDO 完全一样。
一个有趣的观察:
也许值得用一个重复的分数或类似的东西来检查整个事情的表现形式:param_rounded_5_places 和 param_rounded_4_places,它们基本上被计算为 7002 / 3603 ===> 1.943380516...
Computed Expected Obtained
param_rounded 1.945 1.95 1.94 --> Rounded down
param_rounded_5_places 1.943380516 1.94338 1.94338
param_rounded_4_places 1.943380516 1.9434 1.9434 --> NOT Rounded down
可以看出withquotes(和with PDObindParam()没有指定datatype),当精度限制在小数点后两位时问题依然存在,但似乎消失了(至少在这个case) 当精度检查超过小数点后第二位时。这是一个问题吗?我不知道.....
尽管出于此答案的目的,我没有使用mysqli_*() PHP 函数运行相同的测试,但很可能 MySQLi 准备查询的行为方式也相同。
修复:
-
一个明显的解决方法是使用bindParm() 指定数据类型。请参阅mysqli_stmt_bind_param() 获取 MySQLi 等效项:
$stmt->bindParam(1, $array[0], PDO::PARAM_INT);
-
如果由于某种原因更改程序太麻烦,那么间接 解决方法是在查询中使用CAST。例如:
ROUND((ROUND((7002) / CAST(? AS DECIMAL(10,2))) * CAST(? AS DECIMAL(10,2))) / 3600,2)
这个结果可以在param_rounded_modified 中看到,尽管这会以增加查询的复杂性为代价。
总之,我觉得结论会有点扭曲。错误更多是流行约定(绑定而不指定数据类型)的问题,因为这种情况会暴露,并不总是正确的。我们显然不能直接称其为错误(尽管存在如上所述的异常),因为 MYSQL 确实提供了在参数化查询中指定数据类型的方法,即使通常不明确要求(在 PDO 中)相当常用的算术运算。