Laravel 抱怨具有重复命名参数的查询

Laravel complains about query with duplicate named parameters

当我这样做时(laravel):

<?php
\DB::select('SELECT * FROM my_table WHERE id = :id || id = :id', [
    'id' => 1,
]);

它说:

SQLSTATE[HY093]: Invalid parameter number (SQL: SELECT * FROM my_table WHERE id = :id || id = :id)

但是当我这样做时(纯 php):

<?php
$dbh = new PDO('mysql:dbname=...', '...', '...');
$stmt = $dbh->prepare('SELECT * FROM my_table WHERE id = :id || id = :id');
$r = $stmt->execute([
    'id' => 1,
]);
while ($row = $stmt->fetch()) {
    var_dump($row['id']);
}

成功了。我做错了什么?

P.S。显然,我运行遇到问题时的查询更有意义。

UPD 大致真实查询:

SELECT id
FROM objects
WHERE ACOS(
    SIN(RADIANS(lat)) * SIN(RADIANS(:lat))
    + COS(RADIANS(lat)) * COS(RADIANS(:lat)) * COS(RADIANS(:lng - lng))
) * 6371 < 10

据我所知,这一切都归结为 mysql 无法处理命名参数。

mysqli::prepare:

This parameter can include one or more parameter markers in the SQL statement by embedding question mark (?) characters at the appropriate positions.

pdo::prepare:

You must include a unique parameter marker for each value you wish to pass in to the statement when you call PDOStatement::execute(). You cannot use a named parameter marker of the same name more than once in a prepared statement, unless emulation mode is on.

Laravel 具有仿真模式 disabled by default。可以通过在连接设置中添加 'options' => [PDO::ATTR_EMULATE_PREPARES => TRUE] 来在 config/database.php 中启用它。这样您将获得与纯 php 相同的结果。不过不确定这是个好主意。

我通常使用带有 "constant" 派生的 table 的 CROSS JOIN 来解决这个问题(FROM 子句中的子查询)。然后我可以根据需要多次重复使用这些参数。

SELECT id
FROM objects o
CROSS JOIN (SELECT :lat as lat, :lng as lng) params
WHERE ACOS(
    SIN(RADIANS(o.lat)) * SIN(RADIANS(params.lat))
    + COS(RADIANS(o.lat)) * COS(RADIANS(params.lat)) * COS(RADIANS(params.lng - o.lng))
) * 6371 < 10