【问题标题】:mysql query : get the last row of related table on multi table selectionmysql查询:获取多表选择相关表的最后一行
【发布时间】:2019-09-04 01:07:42
【问题描述】:

我有 2 个这样的表格

products

  • 身份证
  • 标题

plans

  • 身份证
  • product_id
  • 价格
  • 类型

基本上这个想法是为每个产品设置多个价格,每个产品的最后一个计划将是它的当前价格,如果它被删除或过期,它将回退到之前的计划

因此,如果一个产品有 2 个 ID 为 (1, 2) 的计划,那么带有 id = 2 的计划将是其当前价格

我想展示他们最后计划的产品type = off

这是由 Laravel ORM Eloquent 生成的 SQL Query

select * from `products` where exists
        (select * from `plans` where `products`.`id` = `plans`.`product_id`
                and `type` = 'off' 
                and `plans`.`deleted_at` is null) 
        and `products`.`deleted_at` is null

问题是它不会检查它会在所有计划中搜索的最后/当前计划...所以即使id = 2 类型的计划没有关闭并且如果plan.id = 1 类型关闭我仍然会将在查询中包含此产品
这是php代码:

$wonder_product = Product::whereHas('CurrentPlan', function ($q) {
    $q->where('type', 'off');
})->get();

【问题讨论】:

标签: php mysql laravel innodb


【解决方案1】:

尝试使用 GROUP BY 子查询:

$wonder_product = Product::whereHas('CurrentPlan', function ($q) {
    $q->where('type', 'off')
    ->whereIn('id', function ($subquery) {
        $subquery
        ->from(with(new CurrentPlan)->getTable())
        ->select(DB:raw('MAX(id)'))
        ->groupBy('product_id');
    });
})->get();

或者,如果您可以使用原始子查询:

$wonder_product = Product::whereHas('CurrentPlan', function ($q) {
    $q->where('type', 'off')
      ->whereRaw('id in (select max(id) from plans group by product_id)')
})->get();

如果我没记错的话,这两种方法都应该生成这样的查询:

select * from `products`
where exists (
        select * from `plans`
        where `products`.`id` = `plans`.`product_id`
          and `type` = 'off' 
          and `plans`.`deleted_at` is null
          and id in (select max(id) from plans group by product_id)
  ) 
  and `products`.`deleted_at` is null

但如果是我,我可能会写一个这样的原始查询:

$wonder_product = Product::hydrateRaw('
    select products.*
    from products
    where 'off' = (
      select plans.type
      from plans
      where plans.product_id = products.id
        and plans.deleted_at is null
      order by plans.id desc
      limit 1
    )
    and products.deleted_at is null
');

【讨论】:

    【解决方案2】:

    你应该改用whereDoesntHave

    return Product::whereDoesntHave('plans', function ($q) {
        $q->where('type', 'off');
    })->with('plans')->get();
    

    工作示例:

    products migration

    Schema::create('products', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('title');
        $table->timestamps();
    });
    

    plans migration

    Schema::create('plans', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->unsignedBigInteger('product_id');
        $table->foreign('product_id')->references('id')->on('products')
                                     ->onDelete('cascade');
        $table->decimal('price');
        $table->string('type');
        $table->timestamps();
    });
    

    Product Model Relationship

    public function plans()
    {
        return $this->hasMany(Plan::class);
    }
    

    Plan Model Relationship

    public function product()
    {
        return $this->belongsTo(Product::class);
    }
    

    Sample Data Seeder

    $productWithOnePlanOff = Product::create([
        'title' => 'A product with one of its plans off'
    ]);
    $productWithOnePlanOff->plans()->createMany([
        ['price' => rand(1, 50), 'type' => 'off'],
        ['price' => rand(50, 100), 'type' => 'on']
    ]);
    $productWithNoPlanOff = Product::create([
        'title' => 'A product with none of its plans off'
    ]);
    $productWithNoPlanOff->plans()->createMany([
        ['price' => rand(1, 50), 'type' => 'on'],
        ['price' => rand(50, 100), 'type' => 'on']
    ]);
    

    查询部分和结果

    WhereHas 查找其 ANY 的相关模型与查询条件匹配的模型

    return Product::whereHas('plans', function ($q) {
        $q->where('type', 'off');
    })->with('plans')->get();
    

    结果

    [
        {
            "id": 1,
            "title": "A product with one of its plans off",
            "created_at": "2019-09-03 16:30:30",
            "updated_at": "2019-09-03 16:30:30",
            "plans": [
                {
                    "id": 1,
                    "product_id": 1,
                    "price": "46.00",
                    "type": "off",
                    "created_at": "2019-09-03 16:30:30",
                    "updated_at": "2019-09-03 16:30:30"
                },
                {
                    "id": 2,
                    "product_id": 1,
                    "price": "50.00",
                    "type": "on",
                    "created_at": "2019-09-03 16:30:30",
                    "updated_at": "2019-09-03 16:30:30"
                }
            ]
        }
    ]
    

    虽然带有 whereDoesntHave 的查询确保其相关模型的 NONE 与查询条件匹配

    return Product::whereDoesntHave('plans', function ($q) {
        $q->where('type', 'off');
    })->with('plans')->get();
    

    结果

    [
        {
            "id": 2,
            "title": "A product with none of its plans off",
            "created_at": "2019-09-03 16:30:30",
            "updated_at": "2019-09-03 16:30:30",
            "plans": [
                {
                    "id": 3,
                    "product_id": 2,
                    "price": "49.00",
                    "type": "on",
                    "created_at": "2019-09-03 16:30:30",
                    "updated_at": "2019-09-03 16:30:30"
                },
                {
                    "id": 4,
                    "product_id": 2,
                    "price": "93.00",
                    "type": "on",
                    "created_at": "2019-09-03 16:30:30",
                    "updated_at": "2019-09-03 16:30:30"
                }
            ]
        }
    ]
    

    希望对你有帮助

    【讨论】:

    • 你确定吗?我想为每个产品检查type=off 的最高id计划...这似乎是在要求没有任何计划的产品
    • 我不确定您要的是什么!您想获取最新添加的套餐类型为“关闭”的产品吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2012-12-26
    相关资源
    最近更新 更多