mysql 查询:在多 table 选择中获取相关 table 的最后一行
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 Eloquent
生成的 SQL 查询
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
问题是它不检查 last/current 计划它会在所有计划中搜索...所以即使 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
');
我有 2 个这样结构的表
products
- id
- 标题
plans
- id
- product_id
- 价格
- 类型
基本上,想法是为每个产品设置多个价格,每个产品的最后一个计划将是其当前价格,如果它被删除或过期,它将回退到以前的计划
因此,如果产品有 2 个计划,id 为 (1, 2)
,则计划 id = 2
将是其当前价格
我想展示他们最后计划的产品type = off
这是由 Laravel ORM 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
问题是它不检查 last/current 计划它会在所有计划中搜索...所以即使 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
');