标记 MySQL 数据库中的大量随机记录 - 仅限一次

Flag large number random records in a MySQL database - one time only

在我的数据库中 table 我有 5 列,id, l_num, s_num, win, claimed。有 415,000 条记录。我如何随机 select 20,000 条记录并在 win 字段中用 1 更新记录?

我知道我可以使用 SELECT * FROM tableName ORDER BY RAND() LIMIT 20000 到 select 那些随机记录,但我也了解到这种类型的 select 对于大量数据来说效率很低。

我只需要在数据库上执行一次。一旦标记了所有记录,我们只需一次检查一个标记。

每条记录被选中的概率为 20000/415000 = 0,04819...。

因此您可以 select 所有 RAND() 小于 0.049 的记录。这将 select 大约 20335 条记录,并且从 这些 你可以 运行 LIMIT 20000.

SELECT * FROM tableName WHERE RAND() < (20000/415000)*1.05 LIMIT 20000;

在上面,我添加了 5% 的边距,以 "reasonably" 确保 select 有足够的记录。

当然,UPDATE 查询也可以这样做,即

UPDATE tableName SET ... WHERE RAND() < (20000/415000)*1.05 LIMIT 20000;

根据实验,此方法 select 使用 1.01 的边距时少于 20000 条记录的概率为 7%,使用 1.02 的边距时为 0.5%,使用 1.03 的边距时低于 0.05%。

此方法的另一个缺点是 selection 并不是真正随机的,因为最后的记录被选中的概率较低(因为 20,000 条记录的配额在之前极有可能被满足完整的 table 已经过检查。

要添加一个 "winning code",您可以使用相同的方法(尽管此时更近似)

... SET win = CASE WHEN RAND() BETWEEN 0 AND .333 THEN 'potting soil' WHEN RAND() BETWEEN .333 AND .666 THEN 'gift certificate' ELSE 'something else' END WHERE RAND() < ...

此处您需要仔细调整值,因为 RAND() 在每次调用时计算一次。因此,如果您想在 3 种可能性之间平分,这将 工作:

CASE WHEN RAND() BETWEEN 0 AND 0.333   THEN 1
     WHEN RAND() BETWEEN .333 AND .666 THEN 2
     ELSE                                   3
END

因为第一种情况将被选择 33% 的次数(这是正确的),那么第二种情况将被选择 剩余 次的 33%,即 2 /9ths,第三个将覆盖其余部分(4/9ths)。要平均划分范围,您需要 33%、50% 和 50%:

mysql> SELECT r, count(*)/1048576.0 FROM (select case when rand() between 0 and 0.333 then 1 when rand() between 0.333 and 0.666 then 2 else 3 end AS r from numbers) AS a GROUP BY r;
+---+--------------------+
| r | count(*)/1048576.0 |
+---+--------------------+
| 1 |             0.3330 |
| 2 |             0.2218 |
| 3 |             0.4452 |
+---+--------------------+
3 rows in set (0.03 sec)

mysql> SELECT r, count(*)/1048576.0 FROM (select case when rand() between 0 and 0.333 then 1 when rand() between 0 and 0.5 then 2 else 3 end AS r from numbers) AS a GROUP BY r;
+---+--------------------+
| r | count(*)/1048576.0 |
+---+--------------------+
| 1 |             0.3324 |
| 2 |             0.3337 |
| 3 |             0.3339 |
+---+--------------------+
3 rows in set (0.03 sec)

请注意,除法只是概率性的 - 您实际上不会完全 1/3、1/3 和 1/3。

另一种可能性是为每条记录分配一个随机整数,具有足够的颗粒度(例如从 0 到 415,000,000 的随机数),对其进行索引,并且(a)通过二等分找到值 K 使得数字指数低于 K 的人恰好有 20,000,(b) 根据取模分配奖品(即 ndx % 100 在 0 到 33 之间的每个人都获得礼券。如果您需要经常 运行 这种操作,为每个参与者分配一个 "destiny" 号码可能是值得的。

如果提取次数与总数相比很大(在本例中就是这样),一定要使用 Salman 更精确的解决方案。

您可以使用以下查询:

UPDATE tableName SET win = 1 ORDER BY RAND() LIMIT 20000