【发布时间】:2011-05-18 18:45:27
【问题描述】:
我怎样才能最好地编写一个从总共 600k 中随机选择 10 行的查询?
【问题讨论】:
-
这里是8 techniques;也许一个会在你的情况下运作良好。
-
(这实际上是 5 种技术——有些没有改进。)
我怎样才能最好地编写一个从总共 600k 中随机选择 10 行的查询?
【问题讨论】:
SELECT
*
FROM
table_with_600k_rows
WHERE
RAND( )
ORDER BY
id DESC
LIMIT 30;
id是主键,按id排序, EXPLAIN table_with_600k_rows,发现该行没有扫描整个表
【讨论】:
我知道这不是你想要的,但我会给你的答案是我在生产中使用的 small website。
根据你访问随机值的次数,不值得使用 MySQL,因为你将无法缓存答案。我们有一个按钮可以访问一个随机页面,如果用户愿意,他可以每分钟点击几次。这将导致大量 MySQL 的使用,至少对我来说,MySQL 是最大的优化问题。
我会采用另一种方法,您可以将答案存储在缓存中。对您的 MySQL 进行一次调用:
SELECT min(id) as min, max(id) as max FROM your_table
使用您的最小和最大 ID,您可以在您的服务器中计算一个随机数。在python中:
random.randint(min, max)
然后,使用你的随机数,你可以在你的 Table 中获得一个随机的 Id:
SELECT *
FROM your_table
WHERE id >= %s
ORDER BY id ASC
LIMIT 1
在这种方法中,您对数据库进行了两次调用,但您可以缓存它们并且长时间不访问数据库,从而提高性能。请注意,如果您的桌子上有洞,这不是随机的。多于 1 行很容易,因为您可以使用 python 创建 Id 并为每一行执行一个请求,但由于它们被缓存,所以没关系。
【讨论】:
从书:
使用偏移量选择随机行
还有一种技术可以避免前面发现的问题 替代方法是计算数据集中的行数并返回一个随机数 介于 0 和计数之间的数字。然后使用这个数字作为偏移量 查询数据集时
$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();
当您无法假设连续的键值和 您需要确保每一行都有被选中的机会。
【讨论】:
SELECT count(*) 会变慢。
我得到 快速查询(大约 0.5 秒),使用 慢速 cpu,在 400K 寄存器 MySQL 数据库非缓存 2Gb 大小中选择 10 个随机行。在这里查看我的代码:Fast selection of random rows in MySQL
$time= microtime_float();
$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);
$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
if($id_in) $id_in.=",$id";
else $id_in="$id";
}
mysql_free_result($rquery);
$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
logger("$id, $url",1);
}
mysql_free_result($rquery);
$time= microtime_float()-$time;
logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);
【讨论】:
ORDER BY RAND() 一样慢
FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';查看。
ORDER BY RAND() 相比的好处是它只对 id 进行排序(而不是整行),因此临时表更小,但仍然必须对所有这些进行排序。
我查看了所有答案,我认为根本没有人提到这种可能性,我也不知道为什么。
如果您想以极低的成本获得最大的简单性和速度,那么对我来说,针对数据库中的每一行存储一个随机数似乎是有意义的。只需创建一个额外的列random_number,并将其默认设置为RAND()。在此列上创建索引。
然后,当您想要检索一行时,在您的代码(PHP、Perl 等)中生成一个随机数并将其与列进行比较。
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
我想虽然它对于单行来说非常整洁,但对于像 OP 要求的十行,你必须分别调用它十次(或者想出一个聪明的调整,让我立即逃脱)
【讨论】:
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
你也可以像这样应用 where 子句
PREPARE stm from 'select * from table where available=true limit 10 offset ?';
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
EDIT:偏移量可能取一个接近表末尾的值,这将导致 select 语句返回更少的行(或者可能只有 1 行),为了避免这种情况,我们可以检查offset 再次声明后,就像这样
SET @rows_count = 10;
PREPARE stm from "select * from table where available=true limit ? offset ?";
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
SET @_offset = (SELECT IF(@total-@_offset<@rows_count,@_offset-@rows_count,@_offset));
SET @_offset = (SELECT IF(@_offset<0,0,@_offset));
EXECUTE stm using @rows_count,@_offset;
【讨论】:
我认为这是一种简单而快速的方法,我在实时服务器上对其进行了测试,并与上面的一些答案进行了比较,并且速度更快。
SELECT * FROM `table_name` WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `table_name` ) ORDER BY id LIMIT 30;
//对 130 行的表耗时 0.0014 秒
SELECT * FROM `table_name` WHERE 1 ORDER BY RAND() LIMIT 30
//对 130 行的表耗时 0.0042 秒
SELECT name
FROM random AS r1 JOIN
(SELECT CEIL(RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 30
//对 130 行的表耗时 0.0040 秒
【讨论】:
具有卓越性能并能处理差距的简单查询:
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
这个对 200K 表的查询需要 0.08s 而普通版本 (SELECT * FROM tbl ORDER BY RAND() LIMIT 10) 在我的机器上需要 0.35s。
这很快,因为排序阶段只使用索引的 ID 列。您可以在说明中看到这种行为:
SELECT * FROM tbl ORDER BY RAND() LIMIT 10:
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
【讨论】:
我使用了 Riedsio 发布的 http://jan.kneschke.de/projects/mysql/order-by-rand/(我使用了返回一个或多个随机值的存储过程的情况):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
INSERT INTO rands
SELECT r1.id
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
在文章中,他通过维护一个表格(使用触发器等...参见文章)解决了导致 不那么随机的结果的 id 中的间隙问题; 我通过向表中添加另一列来解决问题,该列填充了连续的数字,从 1 开始(edit: 此列被添加到由运行时的子查询,不会影响您的永久表):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET @no_gaps_id := 0;
INSERT INTO rands
SELECT r1.id
FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS no_gaps_id FROM random) AS r1 JOIN
(SELECT (RAND() *
(SELECT COUNT(*)
FROM random)) AS id)
AS r2
WHERE r1.no_gaps_id >= r2.id
ORDER BY r1.no_gaps_id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
在文章中我可以看到他不遗余力地优化代码;我不知道我的更改是否/多少会影响性能,但对我来说效果很好。
【讨论】:
@no_gaps_id,不能使用索引,因此如果您查看EXPLAIN 进行查询,则与原始查询相比,子查询有Using filesort 和Using where(无索引)。
以下内容应该是快速、公正且独立于 id 列的。 但不保证返回的行数与请求的行数一致。
SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)
说明:假设您想要 100 行中的 10 行,那么每行有 1/10 的概率被选中,这可以通过 WHERE RAND() < 0.1 实现。这种方法不保证 10 行;但如果查询运行的次数足够多,则每次执行的平均行数将在 10 左右,并且表中的每一行都会被平均选择。
【讨论】:
这速度超级快,即使有间隙也是 100% 随机的。
x 的可用行数SELECT COUNT(*) as rows FROM TABLE
x 之间选择 10 个不同的随机数 a_1,a_2,...,a_10
SELECT * FROM TABLE LIMIT 1 offset a_i for i=1,...,10我在Bill Karwin的SQL Antipatterns一书中发现了这个技巧。
【讨论】:
SELECT column FROM table ORDER BY RAND() LIMIT 10 在 O(nlog(n)) 中。所以是的,这是禁食的解决方案,它适用于任何 id 分布。
x 之后的前 10 行。我认为这不是 10 行的随机生成。在我的回答中,您必须在步骤 3 中执行 10 次查询,即每次执行只获取一行,并且不必担心偏移量是否位于表的末尾。
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
不是有效的解决方案,但有效
【讨论】:
ORDER BY RAND() 比较慢
SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10 需要 0.0010,没有 LIMIT 10 需要 0.0012(在该表中 3500 字)。
如果你想要一个随机记录(无论id之间是否有间隙):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
【讨论】:
我改进了@Riedsio 的答案。这是我在有间隙的大型、均匀分布的表上可以找到的最有效的查询(在从具有 > 2.6B 行的表中获取 1000 个随机行时进行了测试)。
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
让我解开正在发生的事情。
@max := (SELECT MAX(id) FROM table)
MAX(id) 都会产生少量开销SELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
联合可以帮助您将所有内容放入 1 个查询中,这样您就可以避免执行多个查询。它还可以让您节省计算MAX(id) 的开销。根据您的应用程序,这可能很重要或很少。
请注意,这只会获取 id 并以随机顺序获取它们。如果您想做更高级的事情,我建议您这样做:
SELECT t.id, t.name -- etc, etc
FROM table t
INNER JOIN (
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
) x ON x.id = t.id
ORDER BY t.id
【讨论】:
LIMIT 1 更改为 LIMIT 30
LIMIT 1 更改为LIMIT 30 会从表中的随机点连续获得 30 条记录。您应该在中间有 30 个 (SELECT id FROM .... 部分的副本。
Riedsio answer 更有效。我已经尝试在 centos 7 上使用 PHP 7.0.22 和 MariaDB 每秒点击 500 次页面,Riedsio 回答我得到了 500+ 额外的成功响应然后你的回答。
如果有自动生成的 id,我发现一种很好的方法是使用模运算符“%”。例如,如果您需要 70,000 条中的 10,000 条随机记录,则可以通过说每 7 行中需要 1 行来简化这一点。这可以在此查询中简化:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0;
如果目标行除以可用总数的结果不是整数,您将获得比您要求的更多的行,因此您应该添加一个 LIMIT 子句来帮助您修剪结果集,如下所示:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0
LIMIT 10000;
这确实需要全面扫描,但它比 ORDER BY RAND 更快,并且在我看来,它比该线程中提到的其他选项更易于理解。此外,如果写入数据库的系统批量创建行集,您可能不会得到预期的随机结果。
【讨论】:
非常简单的单行查询。
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
【讨论】:
order by rand() 会很慢
另一个简单的解决方案是对行进行排名并随机获取其中一个,使用此解决方案,您不需要在表中包含任何基于“Id”的列。
SELECT d.* FROM (
SELECT t.*, @rownum := @rownum + 1 AS rank
FROM mytable AS t,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM mytable))) AS n
) d WHERE rank >= @cnt LIMIT 10;
您可以根据需要更改限制值以访问任意数量的行,但这主要是连续值。
但是,如果您不想要连续的随机值,那么您可以获取更大的样本并从中随机选择。类似...
SELECT * FROM (
SELECT d.* FROM (
SELECT c.*, @rownum := @rownum + 1 AS rank
FROM buildbrain.`commits` AS c,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM buildbrain.`commits`))) AS rnd
) d
WHERE rank >= @cnt LIMIT 10000
) t ORDER BY RAND() LIMIT 10;
【讨论】:
好吧,如果您的键没有间隙并且它们都是数字,您可以计算随机数并选择这些行。但情况可能并非如此。
所以一种解决方案如下:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
这将基本上确保您在密钥范围内获得一个随机数,然后您选择下一个更大的最佳值。 你必须这样做 10 次。
但这并不是真正随机的,因为您的密钥很可能不会均匀分布。
这确实是一个大问题,而且满足所有要求并不容易解决,如果你真的想要 10 个随机行,MySQL 的 rand() 是你能得到的最好的。
然而,还有另一种解决方案速度很快,但在随机性方面也有折衷,但可能更适合您。在这里阅读:How can i optimize MySQL's ORDER BY RAND() function?
问题是你需要它有多随机。
你能再解释一下,我可以给你一个好的解决方案。
例如,与我合作的一家公司有一个解决方案,他们需要极快的绝对随机性。他们最终使用随机值预先填充数据库,这些随机值是按降序选择的,然后再次设置为不同的随机值。
如果你几乎不更新,你也可以填充一个递增的 id,这样你就没有间隙,并且可以在选择之前计算随机键...这取决于用例!
【讨论】:
Id,然后所有您的随机查询将返回那个Id。
FLOOR(RAND()*MAX(id)) 偏向于返回更大的 id。
所有最佳答案都已发布(主要是那些引用链接 http://jan.kneschke.de/projects/mysql/order-by-rand/ 的答案)。
我想指出另一种加速的可能性 - 缓存。想想为什么你需要得到随机行。可能您想在网站上显示一些随机帖子或随机广告。如果您获得 100 个请求/秒,是否真的需要每个访问者获得随机行?通常将这些 X 随机行缓存 1 秒(甚至 10 秒)是完全可以的。 100 个唯一身份访问者是否在同一 1 秒内获得相同的随机帖子并不重要,因为下一秒另外 100 个访问者将获得不同的帖子集。
使用此缓存时,您还可以使用一些较慢的解决方案来获取随机数据,因为无论您的请求/秒如何,它每秒只会从 MySQL 获取一次。
【讨论】:
这是一个可能对许多人有帮助的游戏规则改变者;
我有一个有 200k 行的表,具有顺序 ID,我需要选择 N 个随机行,因此我选择根据表,我创建了这个脚本来找出最快的操作:
logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();
结果是:
36.8418693542479ms0.241041183472 毫秒0.216960906982ms根据这个结果,order desc 是最快的得到最大id的操作,
这是我对这个问题的回答:
SELECT GROUP_CONCAT(n SEPARATOR ',') g FROM (
SELECT FLOOR(RAND() * (
SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 10) a
...
SELECT * FROM tbl WHERE id IN ($result);
仅供参考:要从 200k 表中获取 10 个随机行,我花了 1.78 ms (包括 php 端的所有操作)
【讨论】:
LIMIT——你可以得到重复。
使用下面的简单查询从表中获取随机数据。
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
【讨论】:
我使用这个查询:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
查询时间:0.016s
【讨论】:
处理多种情况的出色帖子,从简单到间隙,再到带间隙的不均匀。
http://jan.kneschke.de/projects/mysql/order-by-rand/
对于大多数一般情况,您可以这样做:
SELECT name
FROM random AS r1 JOIN
(SELECT CEIL(RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
这假设 id 的分布是相等的,并且 id 列表中可能存在间隙。有关更多高级示例,请参阅文章
【讨论】:
mysqli_fetch_assoc($result) 迭代 10 次?或者这 10 个结果不一定可以区分?
我需要一个查询来从一个相当大的表中返回大量随机行。这就是我想出的。先获取最大记录id:
SELECT MAX(id) FROM table_name;
然后将该值代入:
SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;
其中 max 是表中的最大记录 ID,n 是您希望在结果集中包含的行数。假设是记录 id 中没有间隙,尽管我怀疑如果存在它会影响结果(虽然没有尝试过)。我还创建了这个更通用的存储过程;传入表名和要返回的行数。我在 Windows 2008、32GB、双 3GHz E5450 上运行 MySQL 5.5.38,在有 17,361,264 行的表上,它在 ~.03 秒/~11 秒时相当一致,可以返回 1,000,000 行。 (时间来自 MySQL Workbench 6.1;您也可以根据自己的喜好在第二个 select 语句中使用 CEIL 而不是 FLOOR)
DELIMITER $$
USE [schema name] $$
DROP PROCEDURE IF EXISTS `random_rows` $$
CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN
SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @t = CONCAT(
'SELECT * FROM ',
tab_name,
' WHERE id>FLOOR(RAND()*@max) LIMIT ',
num_rows);
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
然后
CALL [schema name].random_rows([table name], n);
【讨论】:
如何从表中随机选择行:
从这里: Select random rows in MySQL
对“表扫描”的快速改进是使用索引来获取随机 id。
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
【讨论】:
PRIMARY KEY)。
将@redsio 的答案与临时表结合起来(600K 不算多):
DROP TEMPORARY TABLE IF EXISTS tmp_randorder;
CREATE TABLE tmp_randorder (id int(11) not null auto_increment primary key, data_id int(11));
INSERT INTO tmp_randorder (data_id) select id from datatable;
然后取一个版本的@redsios 答案:
SELECT dt.*
FROM
(SELECT (RAND() *
(SELECT MAX(id)
FROM tmp_randorder)) AS id)
AS rnd
INNER JOIN tmp_randorder rndo on rndo.id between rnd.id - 10 and rnd.id + 10
INNER JOIN datatable AS dt on dt.id = rndo.data_id
ORDER BY abs(rndo.id - rnd.id)
LIMIT 1;
如果桌子很大,可以在第一部分过筛:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
版本:您可以保持表tmp_randorder 持久化,称之为datatable_idlist。以特定间隔(天、小时)重新创建该表,因为它也会出现漏洞。如果你的桌子真的很大,你也可以补洞
选择 l.data_id 作为整体 来自 datatable_idlist l left join datatable dt on dt.id = l.data_id 其中 dt.id 为空;
版本:直接在数据表中或在持久性额外表datatable_sortorder 中为您的数据集提供一个 random_sortorder 列。索引该列。在您的应用程序中生成一个随机值(我称之为$rand)。
select l.*
from datatable l
order by abs(random_sortorder - $rand) desc
limit 1;
此解决方案区分具有最高和最低 random_sortorder 的“边缘行”,因此按间隔重新排列它们(每天一次)。
【讨论】:
我想这是最好的方法..
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no
【讨论】:
这就是我的做法:
select *
from table_with_600k_rows
where rand() < 10/600000
limit 10
我喜欢它,因为不需要其他表,编写简单,执行速度非常快。
【讨论】: