我刚刚在MySQL 5.7中遇到了一个奇怪的行为(bug?),它没有出现在MySQL 5.5中。
太尴尬了,我需要一个例子来解释它。
无条件的左连接产生N行(如预期的那样)
左连接一个永不匹配的条件 ALSO产生N行。
### EXAMPLE ###
## CREATE TABLES
create table PCPL (K1 int); ## Table 1
create table AUX (K2 int); ## Table 2
## FILL IN TABLES
insert into PCPL values (1),(2),(3); ## fill main table with 3 values
truncate table AUX; ## No need to do this, just to make things clearer
## TEST 1 : "Dry Left join" => RESULT OK : Resulting Table has 3 rows
select PCPL.K1 as K1 , DERIVED.K2 as K2
from PCPL
LEFT JOIN (select K2, 1 as staticValue from AUX) DERIVED
ON PCPL.K1 = DERIVED.K2;
+------+------+
| K1 | K2 |
+------+------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
+------+------+
3 rows in set (0,00 sec)
## TEST 2 : "Never matching condition" => STRANGE : Resulting Table NOT empty
select PCPL.K1 as K1 , DERIVED.K2 as K2
from PCPL
LEFT JOIN (select K2, 1 as staticValue from AUX) DERIVED
ON PCPL.K1 = DERIVED.K2
where staticValue=1; ##### THIS CONDITION IS NEVER MET SINCE TABLE AUX IS EMPTY
+------+------+
| K1 | K2 |
+------+------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
+------+------+
3 rows in set (0,00 sec)
THIS SHOULDN'T HAPPEN !
MySQL 5.5不会发生此行为
这是我在5.5中忘记设置的5.5中的错误或某些参数吗?
谢谢你的时间!
答案 0 :(得分:2)
这看起来像是子查询合并中的错误。这是一种允许MySQL避免实现子查询的开销的策略。
考虑以下两个查询(第一个是原始查询):
select PCPL.K1 as K1, DERIVED.K2 as K2,
(CASE WHEN DERIVED.K2 IS NULL THEN 'is null' ELSE 'not null' END) as K2_null,
staticValue
(CASE WHEN staticValue IS NULL THEN 'is null' ELSE 'not null' END) as staticValue_null
from PCPL LEFT JOIN
(select K2, 1 as staticValue from AUX) DERIVED
ON PCPL.K1 = DERIVED.K2
where staticValue IS NOT NULL;
这个修改过的表单只有DERIVED
的唯一区别:
select PCPL.K1 as K1, DERIVED.K2 as K2,
(CASE WHEN DERIVED.K2 IS NULL THEN 'is null' ELSE 'not null' END) as K2_null,
staticValue
(CASE WHEN staticValue IS NULL THEN 'is null' ELSE 'not null' END) as staticValue_null
from PCPL LEFT JOIN
(select K2, 1 as staticValue from AUX order by k2 limit 3) DERIVED
-------------------------------------------^XXXXXXXXXXXXXXXXXX
ON PCPL.K1 = DERIVED.K2
where staticValue IS NOT NULL;
order by k2 limit 3
的目的是强制子查询的实现。第一个版本返回三行(不正确)。第二个版本返回零行(正确)。在这两种情况下,子查询都返回相同的行集。
不同之处在于强制实现可以解决问题。因此,这看起来像子查询合并中的错误。
您可以在documentation中阅读有关子查询合并的内容。
一篇小编辑。复杂的数据库将查询解析为描述处理的DAG(有向非循环图)。然后,他们有通过图表推送操作的复杂规则 - 特别是过滤操作和列计算。
MySQL从一个更简单的查询模型开始。开发人员正朝着更复杂的模式迈进。像这样的小错误并不出乎意料,但需要一两个版本来解决它们。
答案 1 :(得分:0)
非常感谢。在等待修复错误的同时,可以通过在变量 optimizer_switch
中取消设置 derived_merge 来防止错误发生SET @@optimizer_switch='block_nested_loop=off';