MySQL中RAND()在INSERT ... SELECT中是语句级求值,每行结果相同;需配合序列表JOIN或存储过程循环调用才能实现逐行独立随机。
MySQL中
为什么不能直接用在
的VALUES子句里
因为
在
语句中会被当作“标量表达式”求值一次,而非对每一行重新计算。结果就是所有插入行的随机值完全相同——比如你本想插100条不同
,最后得到的是100个一模一样的数字。
真正起效的方式是让
绑定到每一行的生成过程,常见做法是配合
构造行集,或在存储过程中循环调用。
使用
生成多行时,必须确保
出现在
列表中,且不被优化器提前折叠(例如避免写成
——这仍可能复用同一随机值)
更稳妥的做法是用
一个数字序列表(如
),再对每行调用
如果没现成序列表,可用
临时凑数(仅限测试环境,不保证行数稳定)
用存储过程实现可控批量插入:注意
变量作用域和
赋值时机
很多人写循环插入时,把
结果先赋给局部变量再插入,结果发现所有行又一样了——这是因为
只执行一次,而循环体里没重算。
正确做法是在每次
前当场调用
,或在
语句内部直接使用(不经过变量中转)。
生成
整数;
才是通用区间公式
避免在循环中用
读取刚插入的ID再做关联操作——除非显式加
,否则事务内自增ID不可见
大批量(>1万行)建议分批次提交,比如每1000行
一次,防止锁表或日志暴涨
PostgreSQL里没有
,得用
且注意
重置问题
PostgreSQL函数名是
,返回
。它和MySQL的
行为基本一致,但有一个关键差异:
会影响后续所有
调用,且在同一个事务中不会自动重置。
如果你在存储过程里反复调用
却没重设种子,可能意外得到重复序列(尤其在调试时手动
后忘了清除)。
生成整数:用
,注意类型转换需显式
避免在
中嵌套
多次——PostgreSQL会为每个
调用单独求值,但若写成
,两个值仍是独立的,这点比MySQL更“诚实”
如需可重现的随机数据(比如回归测试),用
,但务必在事务结束前
性能陷阱:别在WHERE条件里用
筛选,尤其是大表
有人想“随机抽10条记录”,写出
——这会导致全表扫描+全排序,MySQL会为每一行计算
并排序,千万级表直接卡死。
真实场景下,要么用主键范围采样(
再
),要么用应用层分页+随机offset(需容忍空结果)。
MySQL 8.0+支持
,但仅限于近似抽样,且不保证均匀性
如果表有自增主键且无大量删除,可用
多次获取ID再查,比
快一个数量级
任何依赖
的
或
条件,都会让索引失效,执行计划必然走ALL
实际用的时候,最常被忽略的是事务隔离级别对随机值的影响——比如在
下,同一个事务中多次执行
可能返回相同值(取决于引擎实现),而不仅仅是逻辑错误。
RAND()INSERT ... SELECTRAND()INSERT ... SELECTuser_idRAND()SELECTSELECTRAND()SELECTSELECT RAND(), 1 UNION SELECT RAND(), 2JOINnumbersRAND()information_schema.columnsDECLAREINTORAND()SET @var = RAND()INSERTRAND()INSERTDELIMITER $$
CREATE PROCEDURE insert_test_data(IN cnt INT)
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < cnt DO
INSERT INTO users (name, age, score) VALUES (
CONCAT('user_', FLOOR(RAND() * 10000)),
FLOOR(18 + RAND() * 60),
ROUND(50 + RAND() * 50, 2)
);
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;FLOOR(RAND() * N)[0, N)RAND() * (max - min) + minSELECT ... INTOCOMMITCOMMITRAND()random()SEEDrandom()0.0 的DOUBLE PRECISIONRAND()SET SEEDrandom()random()SET SEEDFLOOR(random() * 100)::INTINSERT ... SELECTSELECT random()random()SELECT random(), random()SET SEED TO 0.123RESET SEEDRAND()SELECT * FROM big_table ORDER BY RAND() LIMIT 10RAND()WHERE id BETWEEN FLOOR(RAND() * min_id) AND FLOOR(RAND() * max_id)LIMITTABLESAMPLE(SELECT id FROM t ORDER BY id LIMIT 1 OFFSET FLOOR(RAND() * (SELECT COUNT(*) FROM t)))ORDER BY RAND()RAND()WHEREJOINREPEATABLE READSELECT RAND()