我有以下查询
SELECT t.res, IF(t.res=0, "zero", "more than zero")
FROM (
SELECT table.*, IF (RAND()<=0.2,1, IF (RAND()<=0.4,2, IF (RAND()<=0.6,3,0))) AS res
FROM table LIMIT 20) t
返回类似这样的内容:
这正是您所期望的。但是,一旦删除 LIMIT 20
,我就会收到非常意外的结果(返回的行数超过 20,我将其剪掉以使其更易于阅读):
SELECT t.res, IF(t.res=0, "zero", "more than zero")
FROM (
SELECT table.*, IF (RAND()<=0.2,1, IF (RAND()<=0.4,2, IF (RAND()<=0.6,3,0))) AS res
FROM table) t
旁注:
我使用的是 MySQL 5.7.18-15-log,这是一个高度抽象的示例(真正的查询要困难得多)。
我正在尝试了解发生了什么。我不需要提供解决方法的答案,而无需任何解释为什么原始版本不起作用。谢谢。
更新:
GROUP BY id
也适用于第一种情况,而不是使用 LIMIT
。
最佳答案
该问题是由 MySQL 5.7 中引入的关于如何处理(子)查询中的派生表的更改引起的。
基本上,为了优化性能,某些子查询会在不同时间和/或多次执行,当您的子查询返回不确定结果时,会导致意外结果(例如我的情况 RAND()
)。
有两种简单(同样丑陋)的解决方法可以让 MySQL“具体化”(也称为返回确定性结果)这些子查询: 使用 LIMIT <high number>
或GROUP BY id
两者都强制 MySQL 实现子查询并返回预期结果。
最后一个选项是关闭derived_merge
在 optimizer_switch
变量:derived_merge=off
(确保保留所有其他参数不变)。
进一步阅读:
https://mysqlserverteam.com/derived-tables-in-mysql-5-7/
Subquery's rand() column re-evaluated for every repeated selection in MySQL 5.7/8.0 vs MySQL 5.6
关于MySQL 5.7 RAND() 和 IF() 没有 LIMIT 会导致意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48432654/