02-23无意中在在论坛看到一个帖;具体的问题大概就是MySQL与MariaDB对子查询中order by的查询结果不一样;
具体的问题的描述看查看如下的连接;论坛帖子连接:https://bbs.csdn.net/topics/392517765
然后帖子中所描述的问题,我也比较好奇,所以下面就根据提供的信息来实验一下;下面的实验是在mysql和mariadb环境进行的测试;
MariaDB数据库
1、创建实验表
代码语言:javascript复制wjq@118.89.xxx.xxx [wjqdb]>CREATE TABLE `points` (
-> `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
-> `activity_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '项目',
-> `customer_id` int(10) NOT NULL DEFAULT 0 COMMENT '用户',
-> `point` int(10) NOT NULL DEFAULT 0 COMMENT '积分值',
-> `ranking` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '当前排行',
-> `create_time` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '创建时间',
-> `update_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '最后更新时间',
-> PRIMARY KEY (`id`) USING BTREE,
-> UNIQUE KEY `uni_activity_user` (`activity_id`,`customer_id`) USING BTREE,
-> KEY `idx_activity` (`activity_id`,`point`) USING BTREE
-> ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='积分';
Query OK, 0 rows affected (0.01 sec)
2、插入数据,表中数据内容如下:
3、执行更新操作
代码语言:javascript复制wjq@118.89.xxx.xxx [wjqdb]>UPDATE points AS p,
-> (SELECT @a:=@a 1 AS ranks, a.id as ids FROM points AS a,(SELECT @a:=0) r
-> WHERE a.activity_id = 7241 AND a.point > 0 ORDER BY a.point DESC, a.id ASC) AS b
-> SET p.ranking = ranks WHERE p.id = b.ids;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4 Changed: 4 Warnings: 0
4、验证更新以后的结果
通过上述的查看结果可以发现:
和论坛中发帖者的结果是一样的,这也是发帖者所期望的结果;
但是相同的操作,难道在mysql数据库就不行了吗?结果就不一样了?这么神奇吗?下面就在MYSQL环境测试一下;
MYSQL数据库
1、创建测试表
代码语言:javascript复制root@10.105.54.118 [wjq]>CREATE TABLE `points` (
-> `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
-> `activity_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '项目',
-> `customer_id` int(10) NOT NULL DEFAULT 0 COMMENT '用户',
-> `point` int(10) NOT NULL DEFAULT 0 COMMENT '积分值',
-> `ranking` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '当前排行',
-> `create_time` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '创建时间',
-> `update_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '最后更新时间',
-> PRIMARY KEY (`id`) USING BTREE,
-> UNIQUE KEY `uni_activity_user` (`activity_id`,`customer_id`) USING BTREE,
-> KEY `idx_activity` (`activity_id`,`point`) USING BTREE
-> ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='积分';
Query OK, 0 rows affected (0.00 sec)
2、插入数据,表中数据内容如下:
3、执行更新操作
代码语言:javascript复制root@10.105.54.118 [wjq]>UPDATE points AS p,
-> (SELECT @a:=@a 1 AS ranks, a.id as ids FROM points AS a,(SELECT @a:=0) r
-> WHERE a.activity_id = 7241 AND a.point > 0 ORDER BY a.point DESC, a.id ASC) AS b
-> SET p.ranking = ranks WHERE p.id = b.ids;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4 Changed: 4 Warnings: 0
4、验证更新以后的结果
通过上述的查看结果可以发现:
相同的操作在MariaDB和MYSQL环境查询出来的结果是不一样的,这是为什么呢?
论坛看有人回复说是order by在MariaDB和MYSQL的处理方式是不一样的;具体是怎么不一样的呢?那就查看官方文档的说明:
分别查看了一下MYSQL和MariaDB的官方文档关于 MYSQL数据库关于order by的官方说明: https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html MariaDB数据库关于order by的官方说明: https://mariadb.com/kb/en/library/order-by/ https://mariadb.com/kb/en/library/why-is-order-by-in-a-from-subquery-ignored/
在MariaDB官方说明中找到如下的说明: Query with ORDER BY in a FROM subquery produces unordered result. Is this a bug? Below is an example of this: SELECT field1, field2 FROM ( SELECT field1, field2 FROM table1 ORDER BY field2 ) alias
returns a result set that is not necessarily ordered by field2. This is not a bug. A “table” (and subquery in the FROM clause too) is – according to the SQL standard – an unordered set of rows. Rows in a table (or in a subquery in the FROM clause) do not come in any specific order. That’s why the optimizer can ignore the ORDER BY clause that you have specified. In fact, the SQL standard does not even allow the ORDER BY clause to appear in this subquery (we allow it, because ORDER BY … LIMIT … changes the result, the set of rows, not only their order). You need to treat the subquery in the FROM clause, as a set of rows in some unspecified and undefined order, and put the ORDER BY on the top-level SELECT.
通过对比MYSQL和MariaDB的官方文档的说明,得出如下的结论:
MySQL与MariaDB对子查询语句当中的order by的处理方法不同。MySQL会去执行内层查询的排序子句,但是MariaDB会将这个order by忽略掉,除非在内层查询语句中不仅有order by,还有limit子句,那么这时这个order by是不会被MariaDB忽略的。
那么针对上述实际的问题要怎么解决呢?因为子查询中确实没有使用到limit,那我加上limit是否就会结果不一样呢?大家可以自己实验一下;但是反过来想一下,我的查询结果是未知的,如果使用limit去做限制,在实际的使用中不太现实,那要怎么办呢?只能改写SQL语句了,还能咋办呢?
通过分析:很明显在操作update的时候就出现了问题,也就是说在update中select查询出来的结果就有问题了,那update之后结果就有问题了,那再次查询结果肯定无法满足需求了;下面就是我通过实验针对update语句做了改写,改写之后再次查询就没有问题了;
改写SQL语句:
代码语言:javascript复制root@10.105.54.118 [wjq]>UPDATE points AS p,
-> (SELECT * FROM
-> (SELECT @a:=@a 1 AS ranks, a.id AS ids, a.point FROM points AS a,(SELECT @a := 0) r
-> WHERE a.activity_id = 7241 AND a.point > 0 ORDER BY a.point DESC, a.id ASC) s ) AS b
-> SET p.ranking = b.ranks WHERE p.id = b.ids;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4 Changed: 4 Warnings: 0
改写SQL语句之后,再次执行查询,这时候就和在MariaDB中的查询结果一样了;
完美解决,可喜可贺!