繁体   English   中英

mysql查询:在多表选择中获取相关表的最后一行

[英]mysql query : get the last row of related table on multi table selection

我有2个这样的表格

products

  • ID
  • 标题

plans

  • ID
  • product_id
  • 价钱
  • 类型

基本上,想法是每个产品都有多个价格,每个产品的最后一个计划将是其当前价格,如果其删除或过期,它将退回到先前的计划

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

我想展示其最后计划的type = off

这里是由Laravel ORM生成的SQL查询Eloquent

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();

您应该改用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寻找具有与查询条件匹配的任何相关模型的模型

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"
            }
        ]
    }
]

希望这可以帮助

尝试使用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
');

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM