这条 SQL 如何解释?

2018-10-11 13:10:40 +08:00
 skyworker
如下:

SELECT * FROM users WHERE id = (SELECT floor(RAND() * (SELECT MAX(id) FROM users)))

看起来是获取一条随机的 user 数据, 但是真实运行后, 会发现有时候获取 1 条数据; 有时候为空; 有时候会获取 2 条数据. 超出了我对 SQL 运行机制的理解...
4747 次点击
所在节点    MySQL
31 条回复
jasonyang9
2018-10-11 13:15:27 +08:00
确定`users`表中各记录`id`字段的值是连续的?
owenliang
2018-10-11 13:17:48 +08:00
风骚
skyworker
2018-10-11 13:20:06 +08:00
@jasonyang9 是的,id 连续的, 并且还有出现 3 条数据的可能性
lichao
2018-10-11 13:23:47 +08:00
SELECT * FROM users order by RAND() limit 1;
skyworker
2018-10-11 13:25:18 +08:00
@lichao 这样操作会全表扫描, 效率大打折扣.
Youen
2018-10-11 13:30:54 +08:00
ID 不是 PK? Floor 应该返回单个值的啊..
zhuawadao
2018-10-11 13:31:49 +08:00
我运行了一下没发生多记录的情况,是我数据量不够?
skyworker
2018-10-11 13:32:15 +08:00
@Youen id 是标准的主键, InnoDB 引擎
sxw11
2018-10-11 13:34:31 +08:00
我遇到过一个情况,mysql --》查询条件为 varchar 字段=0 的时候,以 0 开头或者非数字开头的都会查出来。
skyworker
2018-10-11 13:35:58 +08:00
@zhuawadao 你多执行几次, 出现多条数据的几率在 10%左右
rrfeng
2018-10-11 13:36:32 +08:00
id 是主键也不行啊,会有删除的情况。
lichao
2018-10-11 13:37:20 +08:00
set @id = (SELECT floor(RAND() * (SELECT MAX(id) FROM users)));
SELECT id, name, email FROM users WHERE id = @id;
zealzz
2018-10-11 13:37:39 +08:00
小心数据类型的隐式转换和精度丢失,这种情况可能会出现多条。
walkerliu
2018-10-11 13:38:15 +08:00
RAND() 返回值为 0 或者返回值过小,乘以你的 MAX ID 最后 floor 取证也是会是 0,你的主键 id 不会为 0 吧 ?多值返回这个有点奇葩
kenorizon
2018-10-11 13:38:19 +08:00
https://stackoverflow.com/questions/45656145/rand-in-where-clause-in-mysql

应该是进行查询的时候,对数据库中的每一行都会重新计算一遍 floor(RAND() * (SELECT MAX(id) FROM users))
所以即使 id 字段的值从 0 开始且连续,查询结果的数目也是不确定的。
yesterdaysun
2018-10-11 13:38:24 +08:00
实测可以重现, Mysql 5.7 随便找了 26w 条数据的一张表, 确定主键是唯一的, 0~4 条的情况都有, 目测最多的是 0 条,1 条和 2 条的情况, 但是无法解释为什么, 颠覆了我的认知, 我也想知道这个是 bug 还是 feature
kevin2ex
2018-10-11 13:41:14 +08:00
试了一下,很🐂
zhuawadao
2018-10-11 13:41:43 +08:00
@skyworker 对的,可以重现!坐等大神布道。
zealzz
2018-10-11 13:42:56 +08:00
这是我以前关于数据类型隐式转换的提问,你可以参考对照一下看看是不是同样的问题。
https://stackoverflow.com/questions/46235729/implicit-conversion-of-a-numeric-in-mysql
kenorizon
2018-10-11 13:44:39 +08:00
@kenorizon #15
再尝试试了几遍,貌似是 where 的条件里面有 select 的话会多次计算 (SELECT floor(RAND() * (SELECT MAX(id) FROM users)))

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/496669

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX