我有一个大约有1.6亿行的数据库表。
该表格有两列:id
和listing
。
我只需要使用PHP在listing
列中显示1000个随机行,并将它们放入<span>
标记中。像这样:
<span>Row 1</span>
<span>Row 2</span>
<span>Row 3</span>
我一直在尝试用ORDER BY RAND()
来做这件事但是在这么大的数据库上加载需要很长时间,而我却找不到任何其他解决方案。
我希望有一种快速/简单的方法来做到这一点。我无法想象简单回显1000个随机行是不可能的......谢谢!
答案 0 :(得分:2)
这里介绍了两个解决方案。这两个提出的解决方案都只是mysql,可以被任何编程语言用作消费者。 PHP可能会非常慢,但它可能是它的消费者。
更快的解决方案:我可以使用更高级的编程技术,在大约十分之二秒的时间内从一个包含1900万行的表中提取1000个随机行。
更慢的解决方案:使用非电源编程技术大约需要15秒。
顺便说一句,两者都使用我写的HERE数据生成。这就是我的小架构。我使用它,继续在那里看到 TWO 更多自我插入,直到我有19M行。所以我不打算再说一遍。但要获得这些19M行,请查看,并再执行2次插入,并且您有19M行。
首先,方法较慢。
select id,thing from ratings order by rand() limit 1000;
在15秒内返回1000行。
对于刚接触mysql的人,请不要阅读以下内容。
这个描述要复杂一点。它的要点是,您预先计算随机数并生成随机数的in clause
结尾,用逗号分隔,并用一对括号括起来。
它看起来像(1,2,3,4)
,但它中会包含1000个数字。
然后存储它们,并使用它们一次。就像密码学的一次性填充。好吧,不是一个很好的类比,但你明白了我的意思。
将其视为in
子句的结尾,并存储在TEXT列中(如blob)。
为什么世界会想要这样做?因为 RNG (随机数生成器)非常慢。但是用几台机器生成它们可能能够相对快速地生成数千台机器。顺便说一下(你会在我所谓的附录的结构中看到这个,我捕获生成一行需要多长时间。使用mysql大约1秒。但是C#,PHP,Java,任何东西都可以把它放在一起。不是你把它放在一起的方式,而是你想要它的时候。
这个策略的长短不一,当它与一个尚未用作随机列表的行相关联时,将其标记为已使用,并发出一个调用,如
select id,thing from ratings where id in (a,b,c,d,e, ... )
并且in子句中有1000个数字,结果在不到半秒的时间内可用。有效地使用mysql CBO(基于成本的优化器)比将其视为PK上的连接索引。
我将其以摘要形式保留,因为它在实践中有点复杂,但可能包括以下粒子
in
子句(附录D)包含预先计算的随机数的表
create table randomsToUse
( -- create a table of 1000 random numbers to use
-- format will be like a long "(a,b,c,d,e, ...)" string
-- pre-computed random numbers, fetched upon needed for use
id int auto_increment primary key,
used int not null, -- 0 = not used yet, 1= used
dtStartCreate datetime not null, -- next two lines to eyeball time spent generating this row
dtEndCreate datetime not null,
dtUsed datetime null, -- when was it used
txtInString text not null -- here is your in clause ending like (a,b,c,d,e, ... )
-- this may only have about 5000 rows and garbage cleaned
-- so maybe choose one or two more indexes, such as composites
);
为了不把它变成一本书,请参阅我的答案HERE,了解运行重复mysql事件的机制。它将使用附录D中的技术和您想要想到的其他想法推动附录A中所见的表的维护。如重复使用行,存档,删除等等。
存储过程简单地给我1000个随机行。
DROP PROCEDURE if exists showARandomChunk;
DELIMITER $$
CREATE PROCEDURE showARandomChunk
(
)
BEGIN
DECLARE i int;
DECLARE txtInClause text;
-- select now() into dtBegin;
select id,txtInString into i,txtInClause from randomsToUse where used=0 order by id limit 1;
-- select txtInClause as sOut; -- used for debugging
-- if I run this following statement, it is 19.9 seconds on my Dell laptop
-- with 19M rows
-- select * from ratings order by rand() limit 1000; -- 19 seconds
-- however, if I run the following "Prepared Statement", if takes 2 tenths of a second
-- for 1000 rows
set @s1=concat("select * from ratings where id in ",txtInClause);
PREPARE stmt1 FROM @s1;
EXECUTE stmt1; -- execute the puppy and give me 1000 rows
DEALLOCATE PREPARE stmt1;
END
$$
DELIMITER ;
可以与附录B概念交织在一起。但是你想要这样做。但它让你有一些东西可以看看mysql如何在RNG方面自行完成。顺便说一下,对于参数1和2分别为1000和19M,我的机器需要800毫秒。
这个例程可以用开头提到的任何语言编写。
drop procedure if exists createARandomInString;
DELIMITER $$
create procedure createARandomInString
( nHowMany int, -- how many numbers to you want
nMaxNum int -- max of any one number
)
BEGIN
DECLARE dtBegin datetime;
DECLARE dtEnd datetime;
DECLARE i int;
DECLARE txtInClause text;
select now() into dtBegin;
set i=1;
set txtInClause="(";
WHILE i<nHowMany DO
set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,", "); -- extra space good due to viewing in text editor
set i=i+1;
END WHILE;
set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,")");
-- select txtInClause as myOutput; -- used for debugging
select now() into dtEnd;
-- insert a row, that has not been used yet
insert randomsToUse(used,dtStartCreate,dtEndCreate,dtUsed,txtInString) values
(0,dtBegin,dtEnd,null,txtInClause);
END
$$
DELIMITER ;
如何调用上面存储的proc:
call createARandomInString(1000,18000000);
生成并保存1行,如上所述包裹1000个数字。大数字,1到18M
作为一个快速说明,如果要修改存储过程,请取消底部附近的行,用于调试&#34;并将其作为最后一行,在存储过程中运行,然后运行:
call createARandomInString(4,18000000);
...生成4个随机数,最多18M,结果可能看起来像
+-------------------------------------+
| myOutput |
+-------------------------------------+
| (2857561,5076608,16810360,14821977) |
+-------------------------------------+
现实检查。这些是一些先进的技术,我不能指导任何人。但无论如何我想分享它们。但我不能教它。过来。
答案 1 :(得分:1)
你想在php中使用rand
函数。签名是
rand(min, max);
所以,将表格中的行数设为$ var并将其设置为max
。
使用SQL执行此操作的方法是
SELECT COUNT(*) FROM table_name;
然后简单地运行一个循环以使用上述函数生成1000个rand并使用它们来获取特定的行。
如果ID不是连续的,但如果它们是关闭的,您只需测试每个rand ID以查看是否有命中。如果它们相距很远,您可以将整个ID空间拉入php,然后通过类似
的方式从该分发中随机抽样$random = rand(0, count($rows)-1);
表示$rows
中的ID数组。
答案 2 :(得分:1)
如果您的RAND()函数太慢,并且您只需要准随机记录(对于测试样本)而不是真正随机的记录,您可以通过按中间字符排序来创建一个快速,有效随机的组(在索引字段中使用SUBSTRING)。例如,按电话号码的第7位排序......按降序排序......然后按第6位排序......按升序排序......这已经是准随机的。您可以对字符列执行相同操作:人名中的第6个字符将无意义/随机等。
答案 3 :(得分:1)
ORDER BY RAND()是一个mysql函数,可以很好地处理小型数据库,但是如果运行大于10k行的任何东西,你应该在程序中构建函数,而不是使用mysql预制函数或以特殊方式组织数据。 / p>
我的建议:保持你的mysql数据被自动增量id
索引,或者添加其他增量和唯一行。
然后构建一个select函数:
<?php
//get total number of rows
$result = mysql_query('SELECT `id` FROM `table_name`', $link);
$num_rows = mysql_num_rows($result);
$randomlySelected = [];
for( $a = 0; $a < 1000; $a ++ ){
$randomlySelected[$a] = rand(1,$num_rows);
}
//then select data by random ids
$where = "";
$control = 0;
foreach($randomlySelected as $key => $selectedID){
if($control == 0){
$where .= "`id` = '". $selectedID ."' ";
} else {
$where .= "OR `id` = '". $selectedID ."'";
}
$control ++;
}
$final_query = "SELECT * FROM `table_name` WHERE ". $where .";";
$final_results = mysql_query($final_query);
?>
如果缺少160万个数据库中的某些增量ID,则可以轻松添加一个函数来添加另一个随机ID(可能是一个while循环),如果随机选择的ID数组少于所需数量。 / p>
如果您需要进一步的帮助,请告诉我。
答案 4 :(得分:0)
请在select语句中使用mysql rand。您的查询将如下所示
SELECT * FROM `table` ORDER BY RAND() LIMIT 0,1;