一天,小明很着急地在通信工具上说:这边线上出现了个奇怪的问题,麻烦 DBA 大大鉴定下,执行语句 select xx from table_name wheere xxx order by 字段A limit offset;
,
表数据总共 48 条,分页数量正常,但出现告终果混杂的状况,第一页的数据出如今了第二页上;若是 order by 字段B
就不会出现这个现象,怎么会这样呢!html
其实,这个问题很简单,若是你有仔细阅读官档的话。~^_^~mysql
咱们先来看看官档是怎么说的:sql
If multiple rows have identical values in the ORDER BY columns, the server is free to return those rows in any order, and may do so differently depending on the overall execution plan. In other words, the sort order of those rows is nondeterministic with respect to the nonordered columns.ide
One factor that affects the execution plan is LIMIT, so an ORDER BY query with and without LIMIT may return rows in different orders.工具
本次实验使用社区版 MySQL 5.6.26(由于小明出现问题的环境就是这个版本O(∩_∩)O~),下面先建立实验环境和初始化测试数据:测试
root@localhost [(none)]>select @@version; +------------+ | @@version | +------------+ | 5.6.26-log | +------------+ 1 row in set (0.00 sec) root@localhost [(none)]>show variables like "sql_mode"; +---------------+------------------------+ | Variable_name | Value | +---------------+------------------------+ | sql_mode | NO_ENGINE_SUBSTITUTION | +---------------+------------------------+ 1 row in set (0.00 sec) root@localhost [(none)]>create database glon_ho; Query OK, 1 row affected (0.04 sec) root@localhost [(none)]>use glon_ho Database changed root@localhost [glon_ho]>create table glon( -> id int not null auto_increment primary key, -> name varchar(20) not null, -> create_time datetime not null, -> age tinyint unsigned default 18 -> ); Query OK, 0 rows affected (0.01 sec) root@localhost [glon_ho]>INSERT INTO `glon` VALUES (1, 'Eason Chan', '2017-05-02 08:10:10', 19),(2, 'Glon Ho', '2017-05-03 12:10:10', 18),(3, '赵敏', '2017-05-03 14:10:10', 17),(4, 'Jacky Cheung', '2017-05-02 14:00:00', 22),(5, '周芷若', '2017-05-02 14:00:00', 16),(6, 'Andy Lau', '2017-05-02 14:00:00', 50),(7, '至尊宝', '2017-05-02 14:00:00', 20),(8, '刘三姐', '2017-05-02 14:00:00', 19); Query OK, 8 rows affected (0.01 sec) Records: 8 Duplicates: 0 Warnings: 0 root@localhost [glon_ho]>select * from glon; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | +----+--------------+---------------------+------+ 8 rows in set (0.00 sec)
这里建立了一个 glon 表,字段有自增 id, 姓名 name, 年龄 age, 及用户注册时间 create_time。spa
接着来复现问题:code
root@localhost [glon_ho]>select * from glon ORDER BY create_time limit 0, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time limit 4, 4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec)
能够看到两次查询结果中都出现了 id 为 8 的刘三姐,从上面初始化数据来看,总共有 8 条数据,如今不但分页出现重复数据,还丢了一条!server
问题确实重现了,不过先不急,咱们再来试多几组其余的排序方式。htm
root@localhost [glon_ho]>select * from glon ORDER BY create_time,age limit 0, 4; +----+------------+---------------------+------+ | id | name | create_time | age | +----+------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | +----+------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time,age limit 4, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec)
root@localhost [glon_ho]>select * from glon ORDER BY create_time,id limit 0, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time,id limit 4, 4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec)
root@localhost [glon_ho]>select * from glon ORDER BY id limit 0, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY id limit 4, 4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec)
看到,后面的几组排序方式都没有再出现问题了,结合官档,咱们知道 order by 排序的时候,若是排序字段中有多行相同的列值,则排序结果是不肯定的。因此后面的几组组合形式的排序或者是主键 id 的排序,由于惟一性高,因此排序是肯定的,不会出现结果混乱的问题。
那是否是能够就此结束了呢,no way, 咱们再来看下面的实验,继续巩固一下:
root@localhost [glon_ho]>select * from glon ORDER BY age limit 0, 4; +----+------------+---------------------+------+ | id | name | create_time | age | +----+------------+---------------------+------+ | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | +----+------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY age limit 4, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec)
咦,这个排序也只是根据一个字段 age 来排序,怎么就没有出问题呢?不急,还有招:
root@localhost [glon_ho]>insert into glon values (9,'乔峰','2017-05-03 13:10:10',22),(10,'段誉','2017-05-03 15:10:10',19),(11,'郭靖','2017-05-03 17:10:10',20),(12,'黄蓉','2017-05-03 08:10:10',19); Query OK, 4 rows affected (0.01 sec) Records: 4 Duplicates: 0 Warnings: 0 root@localhost [glon_ho]>select * from glon; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 11 | 郭靖 | 2017-05-03 17:10:10 | 20 | | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | +----+--------------+---------------------+------+ 12 rows in set (0.00 sec)
我又给 glon 表新增了几条数据,而后再来看看:
root@localhost [glon_ho]>select * from glon ORDER BY create_time limit 0, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time limit 4, 4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time limit 8, 4; +----+--------+---------------------+------+ | id | name | create_time | age | +----+--------+---------------------+------+ | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 11 | 郭靖 | 2017-05-03 17:10:10 | 20 | +----+--------+---------------------+------+ 4 rows in set (0.00 sec)
根据 create_time 排序,没有问题了,再来:
root@localhost [glon_ho]>select * from glon ORDER BY age limit 0, 4; +----+------------+---------------------+------+ | id | name | create_time | age | +----+------------+---------------------+------+ | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | +----+------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY age limit 4, 4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY age limit 8, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec)
能够看到根据年龄 age 排序,问题出现了。
而后在看看组合的排序:
root@localhost [glon_ho]>select * from glon ORDER BY create_time,id limit 0, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time,id limit 4, 4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time,id limit 8, 4; +----+--------+---------------------+------+ | id | name | create_time | age | +----+--------+---------------------+------+ | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 11 | 郭靖 | 2017-05-03 17:10:10 | 20 | +----+--------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time,age limit 0, 4; +----+------------+---------------------+------+ | id | name | create_time | age | +----+------------+---------------------+------+ | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | +----+------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time,age limit 4, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY create_time,age limit 8, 4; +----+--------+---------------------+------+ | id | name | create_time | age | +----+--------+---------------------+------+ | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 11 | 郭靖 | 2017-05-03 17:10:10 | 20 | +----+--------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY age,id limit 0, 4; +----+------------+---------------------+------+ | id | name | create_time | age | +----+------------+---------------------+------+ | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | +----+------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY age,id limit 4, 4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon ORDER BY age,id limit 8, 4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 11 | 郭靖 | 2017-05-03 17:10:10 | 20 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec)
既然排序不定,那么给排序字段加上索引会不会有用呢?
root@localhost [glon_ho]>alter table glon add index ix_age(age); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 root@localhost [glon_ho]>show create table glon\G *************************** 1. row *************************** Table: glon Create Table: CREATE TABLE `glon` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `create_time` datetime NOT NULL, `age` tinyint(3) unsigned DEFAULT '18', PRIMARY KEY (`id`), KEY `ix_age` (`age`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 1 row in set (0.00 sec) root@localhost [glon_ho]>select * from glon order by age limit 0,4; +----+------------+---------------------+------+ | id | name | create_time | age | +----+------------+---------------------+------+ | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | +----+------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon order by age limit 4,4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from glon order by age limit 8,4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec)
也能够不在 order by 后面加多一列增长惟一性,能够改写成下面的形式:
root@localhost [glon_ho]>select * from (select distinct g.* from glon g order by age) t limit 0,4; +----+------------+---------------------+------+ | id | name | create_time | age | +----+------------+---------------------+------+ | 5 | 周芷若 | 2017-05-02 14:00:00 | 16 | | 3 | 赵敏 | 2017-05-03 14:10:10 | 17 | | 2 | Glon Ho | 2017-05-03 12:10:10 | 18 | | 1 | Eason Chan | 2017-05-02 08:10:10 | 19 | +----+------------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from (select distinct g.* from glon g order by age) t limit 4,4; +----+-----------+---------------------+------+ | id | name | create_time | age | +----+-----------+---------------------+------+ | 10 | 段誉 | 2017-05-03 15:10:10 | 19 | | 8 | 刘三姐 | 2017-05-02 14:00:00 | 19 | | 12 | 黄蓉 | 2017-05-03 08:10:10 | 19 | | 11 | 郭靖 | 2017-05-03 17:10:10 | 20 | +----+-----------+---------------------+------+ 4 rows in set (0.00 sec) root@localhost [glon_ho]>select * from (select distinct g.* from glon g order by age) t limit 8,4; +----+--------------+---------------------+------+ | id | name | create_time | age | +----+--------------+---------------------+------+ | 7 | 至尊宝 | 2017-05-02 14:00:00 | 20 | | 9 | 乔峰 | 2017-05-03 13:10:10 | 22 | | 4 | Jacky Cheung | 2017-05-02 14:00:00 | 22 | | 6 | Andy Lau | 2017-05-02 14:00:00 | 50 | +----+--------------+---------------------+------+ 4 rows in set (0.00 sec)
总之,若是发生了,最简单的方法就是在排序列(如 create time)上加索引,而后在 order by 上明示 primary key,这个问题就很是圆满的解决了。
MySQL 使用 limit 进行分页时,可能会出现重复数据,经过加入 order by 子句能够解决,可是须要注意的是,若是排序字段有相同值的状况下,因为排序字段数据重复,可能会致使每次查询排序后结果顺序不一样,分页仍是会出现重复数据,这时能够加入第二个排序字段,提升排序的惟一性,最好保证排序的字段在表中的值是惟一的,这样就能够少写一个排序字段,增长查询效率,由于 order by 后面有多个排序字段时,没法用到索引。