Laravel 查询构建器 - 从另一个表中选择前 1 行
Posted
技术标签:
【中文标题】Laravel 查询构建器 - 从另一个表中选择前 1 行【英文标题】:Laravel Query builder - select top 1 row from another table 【发布时间】: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;
【问题讨论】:
【参考方案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();
参考:
Laravel Model Laravel Relationship【讨论】:
我知道如何用 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 查询构建器来做到这一点吗? 兄弟,我是个会说话的人?以上是关于Laravel 查询构建器 - 从另一个表中选择前 1 行的主要内容,如果未能解决你的问题,请参考以下文章
Laravel - 使用 Eloquent 查询构建器在选择中添加自定义列