【问题标题】:Laravel Query builder - select top 1 row from another tableLaravel 查询构建器 - 从另一个表中选择前 1 行
【发布时间】:2020-01-19 02:38:19
【问题描述】:

我有这个用于 MySQL 的 SQL 查询,它运行良好。但是我需要使用查询生成器重写它,并且需要完全避免DB::raw(),因为开发数据库与生产数据库不同。我知道远非理想,但不幸的是它就是这样。

SELECT athletes.*, 
     (
         SELECT performance
         FROM performances
         WHERE athletes.id = performances.athlete_id AND performances.event_id = 1
         ORDER BY performance DESC
         LIMIT 0,1
     ) AS personal_best
FROM athletes
ORDER BY personal_best DESC
Limit 0, 100

我正在努力如何重写personal_best 部分。我有运动员的成绩表,我只需要选择每个运动员的最佳成绩作为他的个人最好成绩。

我试图搜索答案,但我找到的所有答案都包括原始添加原始 SQL。

任何想法或提示将不胜感激。

提前谢谢你!

所以我接受了我可能不得不为此使用 Eloquent,但仍然无法取得进展。这是我的代码:

class Athlete extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'athletes';

    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;

    /**
     * Get the performances for the Athelete post.
     *
     * @return HasMany
     */
    public function performances()
    {
        return $this->hasMany('App\EloquentModels\Performance', 'athlete_id');
    }
}

class Performance extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'performances';

    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
}

【问题讨论】:

    标签: mysql laravel laravel-query-builder


    【解决方案1】:

    如果您使用的是原始 SQL,只需使用 GROUP BY 为每个运动员执行 MAX 即可。

    SELECT athletes.*, MAX(performance) AS personal_best
    FROM athletes
    INNER JOIN performances ON athletes.id = performances.athlete_id AND performances.event_id = 1
    GROUP BY athletes.id
    ORDER BY personal_best DESC
    LIMIT 0, 100
    

    Laravel 查询构建器:

    DB::table('athletes')
        ->join('performances', 'athletes.id', '=', 'performances.athlete_id')
        ->where('performances.event_id', '=', 1)
        ->groupBy('athletes.id')
        ->orderBy('personal_best', 'desc')
        ->select('athletes.*',DB::raw('MAX(performance) AS personal_best')
        ->limit(100);
    

    Doc 说我们可以使用max(personal_best),但不确定如何将它与 group by 一起使用。

    恐怕您无法避免在查询生成器中使用DB::raw,但您可以使用eloquent model,正如Shaielndra Gupta 所回答的那样。

    为此,您可以创建模型和关系。

    1. Create Model:
    php artisan make:model Athelete
    php artisan make:model Performance
    
    2. Create relationship between Athelete and Perforamnce.
    
    Update Athelete.php 
    
        /**
         * Get the performances for the Athelete post.
         */
        public function performances()
        {
            return $this->hasMany('App\Performance');
        }
    
    3. Get data(didn't verify by myself)
    $data = Athelete::with('performances',function ($query) use ($eventId){
        $query->max('performance')
        $query->where('event_id',$eventId)
        $query->orderBy('performance');
    })->get();
    

    参考:

    【讨论】:

    • 我知道如何用 sql 解决它,但我需要将 sql 重写为 laravel 查询生成器。如果我没有提供足够的描述,我很抱歉。
    • 所以我转换为 eloquent 但仍然无法获得结果现在我收到错误 mb_strpos() expects parameter 1 to be string, object given 任何想法这可能是小问题
    • 对不起,伙计,现在在办公室,稍后可以查看。
    • @MarekBarta,能否请您出示您的代码。运动员模特。
    【解决方案2】:

    你可以像下面这样使用。

    $sql1 = "(
             SELECT performance
             FROM performances
             WHERE athletes.id = performances.athlete_id AND performances.event_id = 1
             ORDER BY performance DESC
             LIMIT 0,1
         ) AS personal_best";
    
    $sql2 = "SELECT athletes.*,$sql1          
    FROM athletes
    ORDER BY personal_best DESC
    Limit 0, 100";
    
    $result = DB::select($sql2);
    

    【讨论】:

    • 这个对我有用的解决方案让我知道它是否适合你。
    • 我需要避免使用 Raw sql,因为本地数据库与生产数据库不同。这就是为什么我想使用 laravel 查询构建,所以我不必两次编写相同的查询。一次本地,一次生产
    • 如果本地数据库不同,那么您必须通过模型访问表。因此,您可以将表名定义为常量并在 RAW SQL 中使用。
    【解决方案3】:

    database.php 创建一个新连接,例如 mysql_dev 用于开发参数。

    DB::connection('mysql_dev')->table('athletes')
        ->leftJoin('performances','athletes.id','performances.athlete_id')
        ->where('performances.event_id',1)
        ->groupBy('athletes.id')
        ->orderByDesc('personal_best')
        ->select('athletes.*',DB::raw('MAX(performances.performance) AS personal_best')
        ->paginate(100);
    
    

    尝试这样不生,

    DB::connection('mysql_dev')->table('athletes')
        ->leftJoin('performances','athletes.id','performances.athlete_id')
        ->where('performances.event_id',1)
        ->groupBy('athletes.id')
        ->orderByDesc('performances.performance')
        ->select('athletes.*','performances.performance'
        ->paginate(100);
    
    

    【讨论】:

    • @Marek Barta,如果您需要查询生成器,可以这样做。也许你会因为数据库结构而出错,但你可以很容易地修复它。
    • 是的,这就是我正在寻找的更多内容,但是有没有办法避免 DB::raw 部分?我现在处于一个愚蠢的境地。数据库服务器可能会随着时间而改变,所以如果发生这种情况,我想避免重写所有的 quieres
    • 我现在得到 0 个结果。当然这可能是我的错,并试图确保我没有因为数据库结构而犯错,但应该没问题
    • 如果你说表演是错误的,我可以说建造者是错误的。但是 0 结果是相当出乎意料的。看起来 event_id 1 没有性能,但您说原始查询确实有效。您可以从 ->paginate(100) 更改为 ->toSql() 以检查构建器执行真正的 SQL 吗?
    • 好吧,这是有道理的。但即使这样行​​得通,它也无济于事,因为如果 event_id 1 没有性能,我仍然需要该行,但实际性能将是空的,而不是实际性能。这是可能的还是变得越来越复杂?
    【解决方案4】:

    你可以像这样使用 Eloquent ORM

    $data = Athelete::with('performances',function ($query) use ($eventId){
        $query->max('performance')
        $query->where('event_id',$eventId)
        $query->orderBy('performance');
    })->get()
    

    【讨论】:

    • 请问我应该如何制作“Athelete”,如果这是一件微不足道的事情,我很抱歉
    • Athlete 是你的模型代表运动员,在运动员内部,模型定义了与性能表的关系更多你可以看到laravel.com/docs/6.x/eloquent
    • 好的,我知道了。但是你知道如何在不使用 Eloquent ORM 的情况下仅使用 laravel 查询构建器来做到这一点吗?
    • 兄弟,我是个会说话的人?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-05
    • 2012-10-15
    • 1970-01-01
    • 1970-01-01
    • 2017-10-05
    • 2011-07-01
    • 2018-07-17
    相关资源
    最近更新 更多