我正在构建一个 Laravel 应用程序,我可以在其中跟踪公司内销售代理赚取的佣金。每次进行销售时,都会在分数数据库表(分数模型)中创建一条记录。因为佣金的计算方式可能会不时发生变化,所以我还有一个数据库表,其中保存开始日期和结束日期以及相关的佣金。表格看起来像这样:
分数
id | 得分 |
---|---|
1 | 2024-01-01 00:00:00 |
2 | 2024-02-01 00:00:00 |
3 | 2024-02-15 00:00:00 |
4 | 2023-12-01 00:00:00 |
佣金规则
id | 开始 | 结束 |
---|---|---|
1 | 2024-01-01 00:00:00 | |
2 | 2024-02-01 00:00:00 | 2024-02-14 00:00:00 |
3 | 2024-02-01 00:00:00 |
出于高效的原因,我想运行一个查询,该查询为我提供一个包含 Scores.id 和 Commission_rules.id 的数组。现在,我通过循环所有分数来创建数组,并对每个分数的commission_rules 表运行查询以查找关联的规则。但由于分数表有 10k+ 条目,因此需要相当长的时间。如果一个分数有多个匹配的佣金规则,我想返回具有最高 id 的规则,如果没有找到规则,我想返回 null。
所以数组应该看起来像这样:
[
[ 'id' => 1, 'commission_rule_id' => 1],
[ 'id' => 2, 'commission_rule_id' => 3],
[ 'id' => 3, 'commission_rule_id' => 3],
[ 'id' => 4, 'commission_rule_id' => null]
]
我根据分数运行的 Eloquent 查询如下所示:
ValueRule::query()
->where('start', '<=', $score->scored_at)
->where(function ($query) use ($score) {
$query->where('end', '>=', $score->scored_at)
->orWhere('end', '=', null);
})
->orderBy('id', 'DESC')
->first();
我已经尝试过使用左连接,但问题是这会为找到的每个匹配项提供一行。
您可以通过子查询达到您想要的结果:
Score::query()
->select('id')
->addSelect([
'commission_rule_id' => ValueRule::query()
->whereColumn('commission_rules.start', '<=', 'scores.scored_at')
->where(function ($query) {
$query->where('commission_rules.end', '>=', 'scores.scored_at')
->orWhereNull('commission_rules.end');
})
->latest()
->limit(1)
])
->get();
在此之上,您可以更进一步,根据计算出的“commission_rule_id”字段创建动态关系。
class Score extends Model
{
public function currentCommisionRule()
{
return $this->belongsTo(ValueRule::class, 'commission_rule_id');
}
public function scopeAddCurrentCommisionRuleField(Builder $query, string $as = 'commission_rule_id')
{
if (empty($query->getQuery()->columns) {
$query->select(); // default * selection
}
$query->addSelect([
$as => ValueRule::query()
->whereColumn('commission_rules.start', '<=', 'scores.scored_at')
->where(function ($query) {
$query->where('commission_rules.end', '>=', 'scores.scored_at')
->orWhereNull('commission_rules.end');
})
->latest()
->limit(1)
]);
}
现在您可以像任何其他关系一样立即加载此动态关系或查询,模型上不存在“commission_rule_id”的事实隐藏在我们的自定义范围后面。
Score::query()
->with(['currentCommisionRule'])
->addCurrentCommisionRuleField()
->get()