
本文介绍如何使用 Laravel Eloquent 一条查询高效获取指定 SKU 下每个 serial_number 对应的最新(按 created_at 或 updated_at)状态记录,避免 N+1 查询,推荐结合第三方包 eloquent-eager-limit 实现原生级分组取最新逻辑。
本文介绍如何使用 laravel eloquent 一条查询高效获取指定 sku 下每个 serial_number 对应的最新(按 created_at 或 updated_at)状态记录,避免 n+1 查询,推荐结合第三方包 `eloquent-eager-limit` 实现原生级分组取最新逻辑。
在实际业务中,常需从日志型或版本化状态表(如 product_status)中提取“每个设备(serial_number)的最新状态”,尤其当多个设备共享同一 SKU 时。原始代码通过两次查询 + 循环 ->latest()->first() 实现,存在明显性能瓶颈:N 个序列号触发 N 次数据库查询,且无法利用索引高效排序。
✅ 推荐方案:使用 staudenmeir/eloquent-eager-limit 包实现单次分组取 Top-1
该包通过 MySQL 用户变量模拟窗口函数(兼容 MySQL 5.7+),在不依赖 ROW_NUMBER() 的前提下,精准实现“每组取最新一条”语义。
第一步:安装扩展包
composer require staudenmeir/eloquent-eager-limit
第二步:在模型中引入 Trait
<?php // app/Models/ProductStatus.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Staudenmeir\EloquentEagerLimit\HasEagerLimit; // ← 引入 Trait
class ProductStatus extends Model
{
use HasEagerLimit; // ← 启用分组限制能力
}第三步:编写单查询获取每 serial_number 的最新记录
$products = ProductStatus::query()
->where('sku', 'sku-number')
->groupLimit(1, 'serial_number') // 每个 serial_number 取 1 条(按主键或时间默认降序)
->get();⚠️ 注意事项:
-
groupLimit(1, 'serial_number')默认按模型主键id降序取最新;若需按created_at或updated_at排序,请显式添加orderBy:->orderBy('created_at', 'desc') ->groupLimit(1, 'serial_number') - 确保数据库表在
(sku, serial_number, created_at)或(sku, serial_number, id)上有复合索引,大幅提升查询效率:CREATE INDEX idx_sku_serial_created ON product_status (sku, serial_number, created_at DESC);
- 该方案仅适用于 MySQL;如使用 PostgreSQL,可直接用
DISTINCT ON或ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)原生实现。
? 扩展用法:
-
groupLimit(3, 'serial_number')→ 获取每个序列号最近 3 条记录; - 结合
with()预加载关联关系,仍保持单次查询(得益于HasEagerLimit的深度优化)。
总结:告别循环查询,一条 Eloquent 链式调用即可精准、高效、可读地获取分组最新记录——这是处理 SKU-Serial 多对多状态聚合场景的最佳实践之一。











