MySQL 5.7 RAND() 和 IF() 没有 LIMIT 会导致意外结果

标签 mysql

我有以下查询

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

返回类似这样的内容:

result with LIMIT

这正是您所期望的。但是,一旦删除 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

result without LIMIT

旁注:
我使用的是 MySQL 5.7.18-15-log,这是一个高度抽象的示例(真正的查询要困难得多)。
我正在尝试了解发生了什么。我不需要提供解决方法的答案,而无需任何解释为什么原始版本不起作用。谢谢。

更新: GROUP BY id 也适用于第一种情况,而不是使用 LIMIT

更新 2: 根据 zerkms 的要求,我在第二个示例中添加了 t.res = 0t.res + 1 result without LIMIT and two more columns

最佳答案

该问题是由 MySQL 5.7 中引入的关于如何处理(子)查询中的派生表的更改引起的。
基本上,为了优化性能,某些子查询会在不同时间和/或多次执行,当您的子查询返回不确定结果时,会导致意外结果(例如我的情况 RAND() )。
有两种简单(同样丑陋)的解决方法可以让 MySQL“具体化”(也称为返回确定性结果)这些子查询: 使用 LIMIT <high number>GROUP BY id两者都强制 MySQL 实现子查询并返回预期结果。
最后一个选项是关闭derived_mergeoptimizer_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/

相关文章:

mysql - Rails 选择子查询(没有 finder_sql,如果可能)

MySql 左连接多表查询

php - 我如何使用 php 将大约 10000 行数据插入 MySQL

php - MySQL 无法识别 MD5

mysql - 选择按消费金额排序的前 10 位用户

mysql - 按年、月分组时对值进行计数

mysql - Doctrine 2 : Use default 0 values instead of null for relation

php - 从多个表中选择特定数据

mysql - 字段为 0 的子顺序

java - 如何在preparedstatement中使用JDBC通配符创建mysql表