Laravel中orderBy对含数字的字符串字段排序时忽略数值的问题求助
Laravel中orderBy对含数字的字符串字段排序时忽略数值的问题求助
看起来你遇到了字符串类型字段排序时,数字按字符顺序而非数值顺序排列的典型问题——数据库会把整个字段当作纯文本逐个字符比较,所以"11"会排在"2"前面,因为第一个字符"1"比"2"小。
先还原你的问题场景:
你的case_id字段(你提到的Class ID应该是笔误?)存储的是类似NewSyn - 1 OBSSt这样的字符串,当前用orderBy('case_id', 'ASC')得到的排序结果是:
NewSyn - 1 OBSSt NewSyn - 1 OBSSt NewSyn - 1 OBSSt NewSyn - 11 SH-B NewSyn - 11 SH-B NewSyn - 15 SH-O NewSyn - 16 SH-On NewSyn - 18 SH-S NewSyn - 18 SH-S NewSyn - 18 SH-S NewSyn - 19 SHS NewSyn - 19 SHS NewSyn - 2 OBSB NewSyn - 2 OBSB NewSyn - 2 OBSB NewSyn - 2 OBSB
而你预期的是先按前缀文本排序,再按数字的数值大小排序,结果应该是:
NewSyn - 1 OBSSt NewSyn - 1 OBSSt NewSyn - 1 OBSSt NewSyn - 2 OBSB NewSyn - 2 OBSB NewSyn - 2 OBSB NewSyn - 2 OBSB NewSyn - 11 SH-B NewSyn - 11 SH-B NewSyn - 15 SH-O NewSyn - 16 SH-On NewSyn - 18 SH-S NewSyn - 18 SH-S NewSyn - 18 SH-S NewSyn - 19 SHS NewSyn - 19 SHS
解决方案:提取数字部分转为数值后排序
因为Laravel的默认orderBy是按字符串规则排序,所以我们需要借助数据库的字符串处理函数,提取出字段中的数字部分,转换成数值类型再排序。
针对MySQL的写法
如果你的项目用的是MySQL,可以用SUBSTRING_INDEX函数精准提取数字部分:
$cases = Classrooms::where('created_date', '>=', $today) // 先按前缀文本排序(比如"NewSyn"部分),保证同前缀条目排在一起 ->orderByRaw("SUBSTRING_INDEX(case_id, ' - ', 1) ASC") // 提取数字部分转为数值后排序 ->orderByRaw("SUBSTRING_INDEX(SUBSTRING_INDEX(case_id, ' - ', -1), ' ', 1) + 0 ASC") ->get();
这段逻辑的细节:
SUBSTRING_INDEX(case_id, ' - ', 1):取-符号前面的前缀文本(比如"NewSyn")SUBSTRING_INDEX(SUBSTRING_INDEX(case_id, ' - ', -1), ' ', 1):先取-后面的内容(比如"1 OBSSt"),再提取第一个空格前的数字部分(比如"1")+ 0:把提取到的字符串数字转换成数值类型,让数据库按数值大小排序而非字符顺序
如果所有前缀都一致(比如都是"NewSyn")
如果你的case_id前缀没有变化,也可以简化写法,只按数字排序:
$cases = Classrooms::where('created_date', '>=', $today) ->orderByRaw("SUBSTRING_INDEX(SUBSTRING_INDEX(case_id, ' - ', -1), ' ', 1) + 0 ASC") ->get();
其他数据库适配提示
如果你的项目用的是PostgreSQL、SQL Server等其他数据库,字符串处理函数会有差异,比如PostgreSQL可以用正则提取数字:
// PostgreSQL示例 $cases = Classrooms::where('created_date', '>=', $today) ->orderByRaw("SUBSTRING(case_id FROM '\\d+')::INT ASC") ->get();
备注:内容来源于stack exchange,提问作者karen
相关产品推荐
相关产品推荐

