MySQL查询:为每种type值获取最多2条记录
刚好之前处理过类似的分组取Top N的需求,给你分享两种靠谱的实现方式,不管是原生SQL还是Laravel Eloquent都能搞定:
原生SQL实现(推荐:窗口函数方案,现代数据库通用)
这是最简洁高效的写法,支持MySQL 8+、PostgreSQL、SQL Server等大部分现代数据库。核心思路是用窗口函数给每个type分组内的行编号,然后筛选出编号≤2的记录:
SELECT id, type, value FROM ( SELECT id, type, value, -- 按type分组,每组内按id排序并编号 ROW_NUMBER() OVER (PARTITION BY type ORDER BY id) AS row_num FROM example ) AS ranked WHERE row_num <= 2 -- 按type和id排序,和你期望的结果顺序一致 ORDER BY type, id;
如果需要按其他字段排序(比如value),只需要把ORDER BY id改成ORDER BY value即可,灵活性很高。
兼容老版本数据库的原生SQL写法(比如MySQL 5.x)
如果你的数据库版本不支持窗口函数,就用关联子查询的方式,虽然性能略逊于窗口函数,但小数据量下完全够用:
SELECT e1.id, e1.type, e1.value FROM example e1 WHERE ( -- 统计同type下id小于等于当前行的记录数 SELECT COUNT(*) FROM example e2 WHERE e2.type = e1.type AND e2.id <= e1.id ) <= 2 ORDER BY e1.type, e1.id;
Laravel Eloquent 实现
方案一:用窗口函数(推荐)
直接通过查询构造器实现窗口函数的逻辑,和原生SQL对应:
use Illuminate\Support\Facades\DB; // 先构建子查询,给每条记录加上分组内的编号 $rankedQuery = DB::table('example') ->selectRaw('id, type, value, ROW_NUMBER() OVER (PARTITION BY type ORDER BY id) AS row_num'); // 外层筛选编号≤2的记录,只返回需要的字段 $results = DB::table($rankedQuery, 'ranked') ->where('row_num', '<=', 2) ->orderBy('type', 'id') ->select('id', 'type', 'value') ->get();
如果是用Eloquent模型(比如你有Example模型),写法类似:
use App\Models\Example; $results = Example::query() ->fromSub(function ($query) { $query->from('examples') // 模型对应的表名,默认是复数 ->selectRaw('id, type, value, ROW_NUMBER() OVER (PARTITION BY type ORDER BY id) AS row_num'); }, 'ranked') ->where('row_num', '<=', 2) ->orderBy('type', 'id') ->select('id', 'type', 'value') ->get();
方案二:兼容老版本数据库的写法
对应前面的关联子查询,用Eloquent的查询构造器实现:
use App\Models\Example; $results = Example::query() ->where(function ($subQuery) { $subQuery->selectRaw('COUNT(*)') ->from('examples as e2') ->whereColumn('e2.type', 'examples.type') ->whereColumn('e2.id', '<=', 'examples.id'); }, '<=', 2) ->orderBy('type', 'id') ->get();
这样执行后就能得到你期望的结果:每个type最多返回2条记录,不足2条的返回全部。
内容的提问来源于stack exchange,提问作者I am L
相关产品推荐
相关产品推荐

