升级到mysql 5.7从5.5,MyISAM到InnoDB,LEFT JOIN和NULL问题

时间:2016-08-31 10:25:38

标签: php mysql join innodb mysql-5.7

我最近使用MyISAM表将数据库从MySQL 5.5更新到MySQL 5.7 InnoDB表。大多数事情都做得很好,但是有一些特殊的查询会产生奇怪的结果。

背景:建议首先获得批准,然后可以投票。在批准之前,只有建议者才能看到建议。

查询:

SELECT description, suggested_by, voted, votes
FROM shop_suggestions s
LEFT JOIN (SELECT suggestion, 1 AS voted FROM shop_suggestion_votes WHERE student = 60910) AS voted ON (voted.suggestion = s.id)
LEFT JOIN (SELECT suggestion, COUNT(student) AS votes FROM shop_suggestion_votes GROUP BY suggestion) AS votes ON (votes.suggestion = s.id)
WHERE  approved > 0 OR suggested_by = 60910 ORDER BY votes
  • 第一次加入 (左)是看投票人是否已经投了这个
  • 第二个 加入(左)是看总共有多少票

真正奇怪的是,当ORDER BY votes部分存在时,投票的值始终为1,而如果它保持不变,那么它的预期值为NULL有人没有投票。

此行为与我升级之前的行为不同,后者的查询工作正常。我假设某处存在某种逻辑错误,但我无法弄明白。任何帮助将不胜感激!!

2 个答案:

答案 0 :(得分:1)

您的查询对我来说很合适。但是,你可以简化它:

SELECT description, suggested_by, voted, votes
FROM shop_suggestions s LEFT JOIN
     (SELECT suggestion, COUNT(student) as votes,
             MAX(student = 60910) as voted
      FROM shop_suggestion_votes
      GROUP BY suggestion
     ) votes
     ON (votes.suggestion = s.id)
WHERE approved > 0 OR suggested_by = 60910
ORDER BY votes;

如果我不得不推测实际问题,我猜你会过度简化查询并删除了有问题的问题。记录的一些常见(错误)使用的MySQL功能并不总是有效,但无论如何都会使用它们。我们想到的是使用select中不在group by中的列。另一种是使用变量的表达式的评估顺序。你的代码中没有这些,但也许你的原始代码有可疑的东西。

答案 1 :(得分:1)

You found a bug where the optimizer takes a literal directly before knowing if the left join has any results. It is a derivate of a known bug, Bug #77707 Right outer join return wrong result when literal expression involved (the left join-version got merged with this).

It was supposedly fixed in 5.7.8,

Incorrect results could be produced tor views and derived tables on the inner side of an outer join and from which non-nullable expressions such as literals were selected. (Bug #73953, Bug #20841369, Bug #67014, Bug #15967464, Bug #65936, Bug #14358878, Bug #67300, Bug #15936817, Bug #76327, Bug #20708288)

Since the (new) filesort has its own optimizer, it seems the bug still persists there and resurfaces when using order by (before, it happened with or without ordering).

Send a bug report.

To make your query work, you can (apart from Gordons rewritten code) trick mysql into evaluating something (and thus not optimizing the literal away) by using e.g.

...
LEFT JOIN (SELECT suggestion, 
           case when student = student then 1 else null end AS voted 
           FROM shop_suggestion_votes WHERE student = 60910) AS voted 
...