问题出现场景:
目前使用egg框架进行后端业务开发,使用sequlize这个ORM进行数据库的操作;今天,遇到一个需求,要按照一个被数据库定义为VARCHAR的字段code,按照降序进行排列。
// 原始数据 仅展示 code 字段['999', '2', '11', '23', '300', '856', '7869', '66', '88', '1234444', '3333']// 理想结果res -> ['1234444', '7869', '3333', '999', '856', '300', '88', '66', '23', '11', '2']
我按照常规的处理方法,进行操作,输出的结果,大概是这样的。如下:
this.ctx.model.xxx.findAndCountAll({offset,limit,order: [['code', 'DESC']],
});// 结果res -> ['999', '88', '856', '7869', '66', '3333', '300', '23', '2', '1234444', '11']
显然,这样的结果不是我们想要的。这样的排序输出,与前端开发中,字符串数组按照ASCII码排序的结果一致。例如:
// 沿用原始数据
const a = ['999', '2', '11', '23', '300', '856', '7869', '66', '88', '1234444', '3333'];a.reverse();
// 执行结果 -> ['999', '88', '856', '7869', '66', '3333', '300', '23', '2', '1234444', '11']
至于mysql中的排序规则,没有去查,原理应该是类似的。到此,其实也找到了问题的所在,就是数字字符串,并没有按照纯数字的规则进行排序,而是按照字符串的规则进行排序,而字符串的排序规则,又是按照ASCII码进行的;
解决方案:
由于不是后端开发,所以遇到这个问题,有点方,也是各种搜百度;
最后确认了三种处理方法:
1、纯sql语句,简单地在要排序的字段后 *1或者 +1即可;
2、纯sql语句,使用MySQL绝对值函数ABS(字段名);
3、纯sql语句,使用ORDER BY CAST(字段名 AS SIGNED);
// 实现 1SELECT * FROM table1 ORDER BY code + 1 DESC;// 实现 2SELECT * FROM table1 ORDER BY code * 1 DESC;// 实现 3SELECT * FROM table1 ORDER BY ABS(code) DESC;// 实现 4SELECT * FROM table1 ORDER BY CAST(code as SIGNED) DESC;
对比以上纯sql的实现,要通过sequelize进行实现的话,好像只有第3种方法可以进行修改;
接下来就是找sequelize中对应的CAST( * as SIGNED) 的方法了;
在sequelize官方文档中,看到的cast()方法,但是type参数并没有罗列对应的枚举值;参考:http://sequelize.org/master/class/lib/sequelize.js~Sequelize.html
然后查mysql对应的方法入参,发现 SIGNED 对应了int类型。所以最终的的sequelize写法如下:
this.ctx.model.table.findAndCountAll({order: [[sequelize.cast(sequelize.col('code'), 'SIGNED'), 'DESC']],
});
这里还有一个坑,如果只是sequelize.cast('code', 'SIGNED')的话,排序是不生效的;只有写成代码块中的写法,才能正常执行;