【问题标题】:Laravel using avg() function with foreign keyLaravel 使用带有外键的 avg() 函数
【发布时间】:2019-12-19 01:18:58
【问题描述】:

我想获得每种产品的平均费率。

我有一个 rates 表,其中有一个 product 表的外键, rates 表与此类似

当我尝试使用此代码获取产品时:

$stocks = Stocks::with('images:url,color', 'tags:tag', 'sizes', 'rates')
                ->get()
                ->pluckDistant('tags', 'tag')
                ->pluckDistant('sizes', 'size');

它返回这个

[
    {
        "id": 10,
        "name": "name",
        "image": "1564964985mI7jTuQEZxD49SGTce6Qntl7U8QDnc8uhVxedyYN.jpeg",
        "images": [
            {
                "url": "1564964985mI7jTuQEZxD49SGTce6Qntl7U8QDnc8uhVxedyYN.jpeg",
                "color": ""
            },
            {
                "url": "1564964985EV20c1jGvCVCzpCv2Gy9r5TnWM0hMpCBsiRbe8pI.png",
                "color": ""
            },
            {
                "url": "1564964985iFcMox6rjsUaM8CHil5oQ9HkrsDqTrqLNY1cXCRX.png",
                "color": ""
            }
        ],
        "tags": [
            "عطور"
        ],
        "sizes": [],
        "rates": [
            {
                "id": 1,
                "stocks_id": 10,
                "rate": 2
            },
            {
                "id": 2,
                "stocks_id": 10,
                "rate": 4
            }
        ],
    }
]

我怎样才能得到"rates":3 的平均费率,使用雄辩的关系在没有php 处理的情况下通过sql 获取它们?

【问题讨论】:

    标签: mysql laravel eloquent laravel-5.8


    【解决方案1】:

    您可以利用 Appending 之类的东西。假设您有一个 Product 模型,它与 Rate 模型具有 OneToMany 关系。

    你的Product 模型看起来像这样:

    class Product extends Model
    {
        protected $with = ['rates'];
    
    
        protected $appends = ['average_rate'];
    
    
        public function getAverageRateAttribute()
        {
            return $this->attributes['average_rate'] = $this->rates->avg('rate');
        }
    
        public function rates() {
            return $this->hasMany(Rate::class);
        }
    }
    

    现在,无论何时您从数据库中查询您的产品,您都会在结果中附加费率。

    array:7 [▼
      "id" => 1
      "created_at" => "2019-08-12 14:08:09"
      "updated_at" => "2019-08-12 14:08:09"
      "average_rate" => 4.5
      "rates" => array:2 [▶]
    ] 
    

    但是,请注意导致 n+1 问题。如果您使用这种方法,请确保始终预先加载您的 rates

    【讨论】:

    • 完美!非常感谢,但如何删除"rates" 数组?不使用 $visible 有什么办法吗?
    • @WailHayaly 嗯,我不太确定,但如果您想明确说明最终 json 结果的外观,请查看API Resources。我希望这会有所帮助。
    • 你没有附加一个表,你只是在返回的结果中添加了一个“额外的”属性。因此,指定返回类型可以解决问题。 return (int) $this->attributes['average_rate'] = $this->rates->avg('rate');
    • 好答案!我投了赞成票,因为它教会了我一个新功能。然而,平均值是在 PHP 中以 Collection 计算的(来自预先加载的 rates 关系),而不是根据问题在 SQL 中执行。
    • @erich 是的,它会在 SQL 中发生,但在这种特定情况下不建议这样做,它会导致 n+1 问题。假设您使用Product::all()->toArray() 查询所有产品,现在对于每个产品,您的应用程序都将运行select 查询以获取average_rate
    【解决方案2】:

    您可以在费率表上使用联接和聚合函数。

    Stocks::with('images:url,color', 'tags:tag', 'sizes')
     ->join('rates', 'rates.stocks_id', '=', 'stocks.id')
     ->selectRaw('stocks.*')
     ->selectRaw('AVG(rates.rate) as average_rating')
     ->selectRaw('tags.*')
     ->selectRaw('sizes.*')
     ->selectRaw('images.*')
     ->groupBy('stocks.id')
     ->get()
    

    【讨论】:

    • "SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in on clause is ambiguous (SQL: select AVG(stocks_rates.rate) from stocks` 内部连接 ​​stocks_rates on stocks_rates.stocks_id = id group by id)"`
    • 抱歉,请将id 替换为stocks.id
    • [ { "AVG(stocks_rates.rate)": "3.0000", "images": [], "tags": [], "sizes": [] } ]
    • 所以一切都很好,唯一剩下的就是在 select 语句中指定要选择的其他列。例如:Stocks::with('images:url,color', 'tags:tag', 'sizes') ->join('rates', 'rates.stocks_id', '=', 'id') ->selectRaw('AVG(rates.rate) as average_rating') ->selectRaw('tags.*') ->selectRaw('sizes.*') ->selectRaw('images.*) ->groupBy('id') ->get()
    • 我将调整我的答案以提高可编辑性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 2021-07-29
    • 2019-01-24
    • 2018-06-04
    • 1970-01-01
    相关资源
    最近更新 更多