MySQL5.7.20-通过合并列按顺序左连接-非常奇怪的行为
我面临一个非常奇怪的问题,希望你能解释给我听。 我试图做的是根据子查询中的合并列对结果集进行排序。让我解释清楚 我有两张桌子:MySQL5.7.20-通过合并列按顺序左连接-非常奇怪的行为,mysql,join,left-join,coalesce,mysql-5.7,Mysql,Join,Left Join,Coalesce,Mysql 5.7,我面临一个非常奇怪的问题,希望你能解释给我听。 我试图做的是根据子查询中的合并列对结果集进行排序。让我解释清楚 我有两张桌子: CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=ut
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `user_favorites_user` (
`source_user_id` int(11) NOT NULL,
`favorited_user_id` int(11) NOT NULL,
KEY `source_user_id` (`source_user_id`),
KEY `favorited_user_id` (`favorited_user_id`),
CONSTRAINT `user_favorites_user_ibfk_1` FOREIGN KEY (`source_user_id`) REFERENCES `user` (`id`),
CONSTRAINT `user_favorites_user_ibfk_2` FOREIGN KEY (`favorited_user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
当一个用户(假设ID=1)正在浏览该网站时,我想向他展示其他用户的最爱在底部。
因此,我从这个查询开始:
select user.*, coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user
left join (
select 1 as is_favorited, favorited_user_id from user_favorites_user
where source_user_id = '1'
) favorites on favorites.favorited_user_id = user.id
到目前为止还不错,这是我得到的,也是我所期望的:
+----+-------+------------------------+
| id | name | is_favorited_coalesced |
+----+-------+------------------------+
| 3 | user3 | 1 |
| 4 | user4 | 1 |
| 1 | user1 | 0 |
| 2 | user2 | 0 |
+----+-------+------------------------+
4 rows in set (0.00 sec)
现在,我想订购结果集。我认为按条款订货就足够了:
select user.*, coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user
left join (
select 1 as is_favorited, favorited_user_id from user_favorites_user
where source_user_id = '1'
) favorites on favorites.favorited_user_id = user.id
order by is_favorited_coalesced asc
在这一点上,我得到了与上面相同的结果:
+----+-------+------------------------+
| id | name | is_favorited_coalesced |
+----+-------+------------------------+
| 3 | user3 | 1 |
| 4 | user4 | 1 |
| 1 | user1 | 0 |
| 2 | user2 | 0 |
+----+-------+------------------------+
4 rows in set (0.00 sec)
然后我认为coalesce不适合动态排序,所以我添加了一个包装器查询,但结果仍然是一样的
为什么ORDER BY\u favorited\u Coalized不起作用?我错过了什么
编辑:
我尝试使用:
order by coalesce(favorites.is_favorited,0) asc
而不是别名,但我得到了相同的结果:
select user.*, coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user left join ( select 1 as is_favorited, favorited_user_id from user_favorites_user where source_user_id = '1' ) favorites on favorites.favorited_user_id = user.id order by coalesce(favorites.is_favorited,0)
--------------
+----+-------+------------------------+
| id | name | is_favorited_coalesced |
+----+-------+------------------------+
| 3 | user3 | 1 |
| 4 | user4 | 1 |
| 1 | user1 | 0 |
| 2 | user2 | 0 |
+----+-------+------------------------+
4 rows in set (0.00 sec)
编辑2
我发现了另一种奇怪的行为。如果我尝试按ID列排序,我会得到以下结果:
--------------
select user.*, coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user left join ( select 1 as is_favorited, favorited_user_id from user_favorites_user where source_user_id = '1' ) favorites on favorites.favorited_user_id = user.id order by id asc
--------------
+----+-------+------------------------+
| id | name | is_favorited_coalesced |
+----+-------+------------------------+
| 1 | user1 | 1 |
| 2 | user2 | 1 |
| 3 | user3 | 1 |
| 4 | user4 | 1 |
+----+-------+------------------------+
4 rows in set (0.00 sec)
我不知道为什么会这样。
我在使用VirtualBox的windows下的虚拟Fedora25上使用MySQL 5.7.20
编辑3
正如我在评论中所建议的:
mysql> explain select user.*, coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user left join ( select 1 as is_favorited, favorited_user_id from user_favorites_user where source_user_id = '1' ) favorites on favorites.favorited_user_id = user.id order by is_favorited_coalesced asc;show warnings;
+----+-------------+---------------------+------------+-------+----------------------------------+----------------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------------------+------------+-------+----------------------------------+----------------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | user | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | NULL |
| 1 | SIMPLE | user_favorites_user | NULL | range | source_user_id,favorited_user_id | source_user_id | 4 | NULL | 2 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+---------------------+------------+-------+----------------------------------+----------------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `so_test`.`user`.`id` AS `id`,`so_test`.`user`.`name` AS `name`,coalesce(1,0) AS `is_favorited_coalesced` from `so_test`.`user` left join (`so_test`.`user_favorites_user`) on(((`so_test`.`user_favorites_user`.`favorited_user_id` = `so_test`.`user`.`id`) and (`so_test`.`user_favorites_user`.`source_user_id` = '1'))) where 1 order by `is_favorited_coalesced` |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
此外:
编辑4:
我跑过:
mysql> explain select user.*, coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user left join ( select 1 as is_favorited, favorited_user_id from user_favorites_user where source_user_id = '1' ) favorites on favorites.favorited_user_id = user.id order by is_favorited_coalesced asc;show warnings;
+----+-------------+---------------------+------------+-------+----------------------------------+----------------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------------------+------------+-------+----------------------------------+----------------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | user | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | NULL |
| 1 | SIMPLE | user_favorites_user | NULL | range | source_user_id,favorited_user_id | source_user_id | 4 | NULL | 2 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+---------------------+------------+-------+----------------------------------+----------------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `so_test`.`user`.`id` AS `id`,`so_test`.`user`.`name` AS `name`,coalesce(1,0) AS `is_favorited_coalesced` from `so_test`.`user` left join (`so_test`.`user_favorites_user`) on(((`so_test`.`user_favorites_user`.`favorited_user_id` = `so_test`.`user`.`id`) and (`so_test`.`user_favorites_user`.`source_user_id` = '1'))) where 1 order by `is_favorited_coalesced` |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT @@optimizer_switch;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @@optimizer_switch |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
如评论中所述
包括用于快速测试的数据集:
SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `user` (`id`, `name`) VALUES
(1, 'user1'),
(2, 'user2'),
(3, 'user3'),
(4, 'user4');
CREATE TABLE `user_favorites_user` (
`source_user_id` int(11) NOT NULL,
`favorited_user_id` int(11) NOT NULL,
KEY `source_user_id` (`source_user_id`),
KEY `favorited_user_id` (`favorited_user_id`),
CONSTRAINT `user_favorites_user_ibfk_1` FOREIGN KEY (`source_user_id`) REFERENCES `user` (`id`),
CONSTRAINT `user_favorites_user_ibfk_2` FOREIGN KEY (`favorited_user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `user_favorites_user` (`source_user_id`, `favorited_user_id`) VALUES
(1, 3),
(1, 4);
这就是bug(或者至少与之密切相关)
它(以一种非常类似的形式)仍然存在于MySQL 8.0.12中(例如,请参见dbfiddle,尽管它希望在修复后不会显示不正确的行为):虽然它现在实际订购正确(可能是因为您对其进行了计算),但它仍然返回了不正确的is\u favorited
:
select user.*, favorites.is_favorited,
coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user
left join (
select 1 as is_favorited, favorited_user_id from user_favorites_user
where source_user_id = '1'
) favorites on favorites.favorited_user_id = user.id
order by is_favorited_coalesced desc
+----+-------+--------------+------------------------+
| id | name | is_favorited | is_favorited_coalesced |
+----+-------+--------------+------------------------+
| 1 | user1 | | 1 |
| 2 | user2 | | 1 |
| 3 | user3 | | 0 |
| 4 | user4 | | 0 |
+----+-------+--------------+------------------------+
这似乎是一个与(非)物化相关的优化器问题(MySQL 5.7有很多这样的问题)。您可以通过强制实现派生表(例如,通过添加限制
)来解决大多数错误:
正如@RaymondNijland提到的,还有其他解决方法,例如在运行该查询之前禁用派生表与的合并。您还可以使用此功能全局禁用该功能,直到错误得到修复,这样您就不必检查每一个已损坏的查询,只需为已验证不受影响的查询启用该功能(这样它们就可以再次从优化中获益)。对我有效:@juergend它对SqlFIDLE有效,所以我不知道为什么它不能在我的机器上工作。我还发现了另一个奇怪的行为,我现在更新我的问题。谢谢你,你能执行一个
解释吗;显示警告代码>像这样()并以这种方式将结果发布到这里,我们可以了解MySQL优化器是如何重写或处理查询的。。您还可以看到优化器正在使用coalesce(1,0)优化coalesce(favorites.is\u favorited,0)
,因为is\u favorited\u coalesced`还可以将SQL模式发布到MySQL服务器<代码>选择@@sql\U模式
我可以在运行5.7.12的Rextester上复制。在我看来,这是一个错误。谢谢你,我会在这个问题上摔碎我的头好长时间的。标记为已接受。@ruddle我觉得这就是问题所在,为什么我要求使用optimizer\u开关配置。唯一的问题是limit1000000
将始终使用临时触发;使用filesort远远不是最佳的,更好的方法是使用设置会话优化器\u switch='derived'u merge=off'代码>见演示。@RaymondNijland虽然有几种方法可以解决这个问题,但开关和限制
的行为应该完全相同(我认为它们不应该引发比限制
更多的文件排序,当然在派生表中没有order by
)。使用limit
的优点是,它可以在需要时通过手术使用(不是每个派生表,甚至可能不是在同一查询中都需要修复),并且不需要第二次查询,而开关可以全局使用,无需检查每个查询是否已损坏。我将在答案中添加该选项。“虽然有几种方法可以解决此问题,但开关和限制都应与sam完全相同”,在这种情况下,选择“是”。。因为别名列上的查询顺序(当然会使用“Using filesort”,当优化器认为需要对大量记录进行排序时,也可以使用“Using temporary”)更好的方法是使用EXPLAIN
检查这两种变通方法,以确保哪种变通方法生成最佳查询计划。。“我认为它们不应该引发超过限制的更多文件排序,在派生表中没有顺序当然”我更担心组合“使用文件排序;使用temporary“在将临时表写入磁盘时,它可能会变成大量的随机磁盘I/O…”和LIMIT
,其中包含大量始终使用的“Using filesort”;在解释中使用“临时”。。。
select user.*, favorites.is_favorited,
coalesce(favorites.is_favorited,0) as is_favorited_coalesced from user
left join (
select 1 as is_favorited, favorited_user_id from user_favorites_user
where source_user_id = '1' limit 1000000
) favorites on favorites.favorited_user_id = user.id
order by is_favorited_coalesced desc
+----+-------+--------------+------------------------+
| id | name | is_favorited | is_favorited_coalesced |
+----+-------+--------------+------------------------+
| 1 | user1 | 1 | 1 |
| 2 | user2 | 1 | 1 |
| 3 | user3 | | 0 |
| 4 | user4 | | 0 |
+----+-------+--------------+------------------------+