MySQL多字段排序的问题

最近在一次开发过程中发现了一个问题,我们都知道MySQL在Order By的时候可以指定多列,会按照指定的顺序依次排序。

例如指定A,B,C列,MySQL会先按A排序,如何A值相同的再按B排序,B值相同的再按C排序。

然后其中还是有不少门道的,例如我就碰到了如下问题,我有一个表test,表结构如下:

1
2
3
4
5
6
7
8
9
10
11

CREATE TABLE `test` (

`A` varchar(128) NOT NULL COMMENT 'A',

`B` varchar(64) NOT NULL COMMENT 'B',

`C` varchar(64) NOT NULL COMMENT 'C'

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='TEST'

其中有如下数据:

A B C
C料 12321 123
c料 测试 SPHC
c料 测试 SPHC
C料 测试 测试
C料 测试 测试

接着执行如下查询

1
2
3

select * from test order by A,B,C

按照我们上面的说法最后出来的结果应该是:

A B C
C料 12321 123
C料 测试 测试
C料 测试 测试
c料 测试 SPHC
c料 测试 SPHC

然而实际出来而结果是:

A B C
C料 12321 123
c料 测试 SPHC
c料 测试 SPHC
C料 测试 测试
C料 测试 测试

可以看到的是A列明显乱序了(注意C大小写),不是说好A相同的排序在一起呢,怎么看起了不对了。

既然是按照列的顺序一个一个排序的,那我们就一个一个排除,看看到底是在哪一列排序出了问题。

执行如下查询:

1
2
3

select * from test order by A,B

得到如下结果:

A B C
C料 12321 123
C料 测试 测试
C料 测试 测试
c料 测试 SPHC
c料 测试 SPHC

也就是说在C列加入排序之前,A还是看着有序的,那到底是怎么回事呢?注意回到最开始我们说的MySQL的排序规则,A相同再按B排,B相同按C排。

所以C在加入排序之前,MySQL认为A,B排序后有相同的结果,也就是c料,测试=C料,测试了,所以C加入排序之后可以在前者相同的排序结果中在按C排序。

于是就产生了看起来错误的顺序,这是因为创建表的时候,没有对字段A,B,C指定collation(MYSQL排序依据),默认会使用对应字符集的Collation,

我这里字符集是utf8mb4,对应的默认collation通常是utf8mb4_general_ci,这个collation是大小写不敏感的。

解决办法有两种:

1.更改字段的collation,

1
2
3
4
5
6
7
8
9
10
11

CREATE TABLE `test` (

`A` varchar(128) NOT NULL COMMENT 'A' COLLATE utf8mb4_bin,

`B` varchar(64) NOT NULL COMMENT 'B' COLLATE utf8mb4_bin,

`C` varchar(64) NOT NULL COMMENT 'C' COLLATE utf8mb4_bin

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='TEST'

使用utf8mb4_bin,这个是大小写敏感的。

2.在排序的时候指定BINARY关键字

1
2
3

select * from test order by binary A,binary B, binary C

因为它会强制MySQL将字段作为二进制字符串对待。

由于这是一个历史存在的表,避免产生其它意向不到的结果,我使用了方法二。至此问题解决。

作者

太阳当空赵先生

发布于

2024-04-25

更新于

2024-04-25

许可协议

评论