【问题标题】:Getting just the minimum value on a joined table with Eloquent使用 Eloquent 获取连接表的最小值
【发布时间】:2020-06-14 15:24:40
【问题描述】:

我有 2 张桌子,subscriptionssubscription_options 就像这样。

subscriptions
------------------------
id    |subscription_name
------------------------
1     |silver
2     |gold


----------------------------------------------------
subscription_options
----------------------------------------------------
id    |subscription_id   |price   |duration (months)
1     |1                 |10      |3
2     |1                 |20      |6
3     |2                 |40      |3
4     |2                 |50      |6

subscriptionssubscription_options 具有一对多关系

// Subscription model
public function options() {

    return $this->hasMany('App\SubscriptionOption', 'subscription_id');
}

我想为我的两个订阅获得最便宜的价格,所以我应该得到 2 行 subscription_options.id13 以及它们的所有列。我将如何用 laravel eloquent 做到这一点?

这些是我尝试过的东西,它们要么给了我一个错误,要么返回了一些接近我正在寻找的东西

Subscription::with('options')->min('price')->get();


Subscription::with(['options' => function($query) {
         $query->min('price');
     }])->get();

【问题讨论】:

    标签: laravel eloquent orm


    【解决方案1】:

    有很多方法可以做到,这取决于程序员的喜好......

    其中之一是创建一个仅获得最便宜价格的子查询,然后使用 subQuery join 加入该子查询

    $subQuerysubscription_options=Subscription_option::
    selectRaw("id as cheapest_option_id,min(price) as cheapest_price")->groupBy('id');
    
    
    
    
          $subscription = Subscription::
    join('subscription_options','subscription_options.subscription_id','subscriptions.id')->
                joinSub($subQuerysubscription_options, 'subQuerysubscription_options', function ($join) {
                    $join->on('subscription_options.id', '=', 'subQuerysubscription_options.cheapest_option_id');
                })
                ->
                select('subscriptions.*','subscription_options.*')
    
            ->get();
    

    由于我们使用的是“join”而不是“leftJoin”,因此查询将保证只有与 subQuery 中的行匹配的行才会得到结果

    选项 2:

    $minCheapestIds=Subscription_option::
    selectRaw("id as cheapest_option_id,min(price) as cheapest_price")->groupBy('id')->pluck('cheapest_option_id');
    

    现在我们有一个最便宜的订阅选项 ID 列表...

      $values=  Subscription::with(['options' => function($query) use($minCheapestIds){
                 $query->whereIn('subscription_options.id',$minCheapestIds);
             }])->get();
    

    这意味着在最便宜的地方加载 Subscription 的选项..

    【讨论】:

    • 哇,有没有更简单或者更优雅的东西?这似乎需要大量资源。
    • 这里是选项 2
    【解决方案2】:

    感谢@OMR 和@unclexo 的回答。您的回答帮助我得到了我正在寻找的答案。这是我最终使用的解决方案。

    控制器

    $subscription_plans = Subscription::with(['orderedOptions' => function($query) {
        $query->min('price');
    }])->get();
    

    型号(订阅)

    public function orderedOptions() {
            return $this->hasOne('App\SubscriptionOption', 'subscription_id')->orderBy('price', 'asc');
    }
    

    基本上,我将模型中的查询结果排序为升序。然后我使用控制器中的min() 函数以最便宜的价格获得记录。在给出的答案没有任何问题的地方,我正在寻找一个更简短、更严格的解决方案。

    经过研究,ORM 比您的普通查询构建器慢。但是如果是做简单的查询和普通的CRUD操作的话,ORM和查询生成器基本没有区别。现在,如果您的查询很复杂并且需要处理成百上千的数据,那么查询构建器就是您的最佳选择。

    【讨论】:

    • 你的代码只得到 (min('price')) 而你的问题需要完整的列列表,我引用“subscription_options.id of 1 和 3 以及它们的所有列。”,无论如何,不客气……
    • @OMR 对,min('price') 在我的模型中过滤来自 groupBy 的结果。最后,它仍然拥有所有的列。
    • 不推荐将hasOne用于一对多关系,一对多关系应该两边都取(hasMnay或belongsTo)这种使用有副作用,反正在你的方法中,没有需要 $query->min('price'); ,去掉它,结果还是一样的!
    • @OMR 哦,我不知道它会有副作用。你能详细说明一下吗?
    • 看看这个(使用 hasOne 而不是 hasMany):stackoverflow.com/questions/62196091/…
    【解决方案3】:

    一般来说,查询构建器比 ORM 更快(在本例中为 Eloquent)。因此,您可以使用查询生成器来获取预期数据,如下所示:

    $cheapestSubscriptions = DB::table('subscription_options')
        ->select('subscription_id', DB::raw('MIN(price) as cheapest_price'))
        ->groupBy('subscription_id')
        ->get();
    

    【讨论】:

    • 为什么查询生成器比 ORM 快?有基准吗?
    • 嗯,ORM 提供了流畅的接口来进行 SQL 查询,但是 ORM 提供的接口需要时间才能将其转换为 SQL。只需搜索“为什么查询生成器比 ORM 更快?”你会得到你想要的答案。
    猜你喜欢
    • 2014-08-12
    • 2020-11-06
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    • 2013-05-17
    相关资源
    最近更新 更多