使用 Laravel Eloquent 和命名绑定的 SQL 查询:混合命名参数和位置参数

Posted

技术标签:

【中文标题】使用 Laravel Eloquent 和命名绑定的 SQL 查询:混合命名参数和位置参数【英文标题】:SQL query with Laravel Eloquent and named bindings : mixed named and positional parameters 【发布时间】:2020-04-27 12:25:18 【问题描述】:

我正在尝试执行以下查询。注意我必须在这个查询中使用 named parameters。 我不明白这个问题,因为我似乎只使用命名参数。如何使命名参数起作用?

注意:当我 在 DB 中执行它 或在 laravel 中使用简单的 DB::table(users)->select( $sql_with_values_inside )强>

我是 Laravel 的新手,所以也许我做错了。

查询

$latitude = (float)$latitude;
$longitude = (float)$longitude;
$radius = (float)$radius;

$sql = '`firstname`,`lastname`,`email`,
     ST_X(`coordinates_public`) AS latitude,
     ST_Y(`coordinates_public`) AS longitude,
     (6371 * ACOS(COS(RADIANS(:latitude)) * COS(RADIANS(ST_Y(coordinates_private))) * COS(RADIANS(ST_X(coordinates_private)) - RADIANS(:longitude)) + SIN(RADIANS(:latitude)) * SIN(RADIANS(ST_Y(coordinates_private))))) AS distance';

$washers = DB::table('users')
    ->selectRaw($sql, ['latitude' => $latitude,'longitude'=> $longitude,'radius'=> $radius])
    ->whereRaw('MBRContains ( LineString (
        Point (
            :longitude + :radius / (111.320 * COS(RADIANS(:latitude))),
            :latitude + :radius / 111.133
        ),
        Point (
            :longitude - :radius / (111.320 * COS(RADIANS(:latitude))),
            :latitude - :radius / 111.133
        )), `coordinates_private`
    )', ['latitude' => $latitude,'longitude'=> $longitude,'radius'=> $radius])
    ->having('distance < :radius', ['radius'=> $radius])
    ->orderBy('distance')
    ->get();

错误

Illuminate\Database\QueryException: SQLSTATE[HY093]:
Invalid parameter number: mixed named and positional parameters (SQL: select `firstname`,`lastname`,`email`,
    ST_X(`coordinates_public`) AS latitude,
    ST_Y(`coordinates_public`) AS longitude,
    (6371 * ACOS(COS(RADIANS(:latitude)) * COS(RADIANS(ST_Y(coordinates_private))) * COS(RADIANS(ST_X(coordinates_private)) - RADIANS(:longitude)) + SIN(RADIANS(:latitude)) * SIN(RADIANS(ST_Y(coordinates_private))))) AS distance from `users` where MBRContains (
    LineString
        (
        Point (
            :longitude + :radius / (111.320 * COS(RADIANS(:latitude))),
            :latitude + :radius / 111.133
        ),
        Point (
            :longitude - :radius / (111.320 * COS(RADIANS(:latitude))),
            :latitude - :radius / 111.133
        )
     ),
    `coordinates_private`) having `distance < :radius` = 4.8312518210935 order by `distance` asc)

【问题讨论】:

感谢@TsaiKoga,?标记 非常适合我的问题。请注意,我还必须通过使用 havingRaw() 来修复我的 having() 子句,以便能够使用 distance 别名。 【参考方案1】:

来自 php 手册:

当您调用 PDOStatement::execute() 时,您必须为希望传递给语句的每个值包含一个唯一的参数标记。除非打开了仿真模式,否则您不能在准备好的语句中多次使用同名的命名参数标记。

所以你不能使用同名的命名参数标记。

有两种解决方案:

    对相同的值使用不同的命名绑定键
$sql = "(6371 * ACOS(COS(RADIANS(:lat)) * COS(RADIANS(ST_Y(coordinates_private))) * COS(RADIANS(ST_X(coordinates_private)) - RADIANS(:long)) + SIN(RADIANS(lat2)) * SIN(RADIANS(ST_Y(coordinates_private))))) AS distance";
...
->selectRaw($sql, ["lat" => $latitude, "long" => $longitude, "lat2" => $latitude])

    使用?代替命名绑定绑定数组
$sql = '(6371 * ACOS(COS(RADIANS(?)) * COS(RADIANS(ST_Y(coordinates_private))) * COS(RADIANS(ST_X(coordinates_private)) - RADIANS(?)) + SIN(RADIANS(?)) * SIN(RADIANS(ST_Y(coordinates_private))))) AS distance';
...
->selectRaw($sql, [$latitude, $longitude, $latitude])

【讨论】:

【参考方案2】:

我不确定这是否是问题,但据我所知,您不能多次使用相同的参数名称,例如对于这部分查询:

:longitude + :radius / (111.320 * COS(RADIANS(:latitude))),

你应该使用:

:longitude + :radius / (111.320 * COS(RADIANS(:latitude2))),

然后传递3个参数:

['latitude' => $latitude,'longitude'=> $longitude, 'latitude2' => $latitude]

当然,您应该为查询的其他部分尝试同样的事情。请记住,我对此不是 100% 确定的,因为我很少使用命名参数。

【讨论】:

【参考方案3】:

在选择中您没有:radius,但尝试绑定它。 应该是

    ->selectRaw($sql, ['latitude' => $latitude,'longitude'=> $longitude])

【讨论】:

以上是关于使用 Laravel Eloquent 和命名绑定的 SQL 查询:混合命名参数和位置参数的主要内容,如果未能解决你的问题,请参考以下文章

遍历数组集合并返回命名键值对(Laravel/Eloquent)

Laravel Eloquent ORM 关系命名约定

Laravel 5 Eloquent:如何获取正在执行的原始 sql? (带有绑定数据)

Laravel Eloquent 序列化:如何重命名属性?

Angular JS 与 Laravel - 如何绑定 Eloquent ORM 数组以查看

Laravel:重命名数据库表中断功能