为何以CURDATE为种子的MySQL随机选行存储过程仅返回3个固定值?
问题原因
你的存储过程存在以下几个核心问题:
- 错误假设主键id连续:你的逻辑默认
quotes表的id是从1开始、无缺口连续的,但只要你曾经删除过数据、或者插入失败过导致自增主键跳号,生成的随机数大部分都会对应到不存在的id,只会匹配到少数落在有效id区间的记录,这是你只能拿到3条引语的核心原因。 - 随机数取值存在无效区间:
FLOOR(RAND()*(@num_rows+1))生成的是0到@num_rows的整数,如果你的id从1开始,0就是完全无效的取值,会导致部分请求匹配不到任何内容。 - 会话变量存在潜在风险:你用了
@开头的用户会话变量,这类变量是数据库连接级别的,如果你的PHP用了长连接复用会话,极端情况下会出现变量被同连接的其他操作篡改的问题。
你手动测试没问题是因为测试次数少,刚好生成的随机数都落在了有效id范围内,PHP调用频率高,很快就暴露了id不连续的问题。
修复方案
直接抛弃依赖id连续的随机数生成逻辑,用ORDER BY RAND(seed)的方式取当日固定的单条记录即可,修复后的存储过程代码如下:
DELIMITER ;; CREATE DEFINER=`root`@`%` PROCEDURE `quote_of_the_day`() BEGIN DECLARE selected_id INT; -- 按当日日期为种子排序,取第一条,完全不依赖id连续性 SELECT id INTO selected_id FROM quotes ORDER BY RAND(CURDATE()) LIMIT 1; -- 返回选中的引语 SELECT quote FROM quotes WHERE id = selected_id; -- 更新统计字段 UPDATE quotes SET quoted = quoted + 1 WHERE id = selected_id AND last_used <> CURDATE(); UPDATE quotes SET last_used = CURDATE() WHERE id = selected_id; END;; DELIMITER ;
内容的提问来源于stack exchange,提问作者just_another_question
相关产品推荐
相关产品推荐

