(PHP)MySQL随机行具有order by和一定范围的大表

(PHP)MySQL随机行具有order by和一定范围的大表,php,mysql,sql,database,Php,Mysql,Sql,Database,我有这张桌子: person_id int(10) pk fid bigint(20) unique points int(6) index birthday date index 4 FK columns int(6) ENGINE = MyISAM 重要信息:该表包含800多万行,并且正在快速增长(目前每天150万行) 我想要什么:当我按点排序表格时,在一定范围内选择4个随机行 我现在是如何做到的:在PHP中,我将某个范围随机化,假设这给了我20%的低范围和30%的高范围。接下来我计算(*

我有这张桌子:

person_id int(10) pk
fid bigint(20) unique
points int(6) index
birthday date index
4 FK columns int(6)
ENGINE = MyISAM
重要信息:该表包含800多万行,并且正在快速增长(目前每天150万行)

我想要什么:当我按点排序表格时,在一定范围内选择4个随机行

我现在是如何做到的:在PHP中,我将某个范围随机化,假设这给了我20%的低范围和30%的高范围。接下来我计算(*)表中的行数。确定最低行数后:表计数/100*低范围。同样适用于高量程。在我使用rand(最低的行,最高的行)计算一个随机行之后,它给了我一个范围内的行号。最后,我通过执行以下操作选择随机行:

SELECT * FROM `persons` WHERE points > 0 ORDER BY points desc LIMIT $random_offset, 1;
点>0在查询中,因为我只需要至少有1个点的随机数

上面的查询运行大约需要1.5秒,但因为我需要4行,所以需要6秒以上,这对我来说太慢了。我认为按点排序花费的时间最多,所以我在考虑创建表的视图,但我对视图没有任何经验,所以你怎么看?视图是一个好的选择还是有更好的解决方案

添加:

我忘了说所有行都有相同的被选中的机会是很重要的

谢谢,我感谢你的帮助!:)


Kevin

您的查询速度非常慢,而且会以指数级的速度变慢,因为在这里使用
LIMIT
会强制它进行完整表排序,然后进行完整表扫描以获得结果。相反,您也应该在PHP端这样做(这种对
LIMIT
的“滥用”实际上是它是非标准SQL的原因,例如MSSQL和Oracle不支持它)

首先确保
点上有索引。这将使
从persons
中选择max(points)、min(points)成为一个立即返回的查询。接下来,您可以根据这两个结果确定点的范围,并使用
rand()
确定请求范围内的4个点。然后对每个结果重复以下步骤:

SELECT * FROM persons WHERE points < $myValue ORDER BY points DESC LIMIT 1
从点数小于$myValue ORDER BY points DESC LIMIT 1的人员中选择*

由于它只需要检索一行,并且可以通过索引确定哪一行,因此执行时间也将以毫秒为单位。

视图不会对您的性能产生任何帮助。我的建议是简单地运行:

SELECT * FROM `persons` WHERE points BETWEEN ? AND ?
确保你有一个关于点的索引。此外,如果适用,您应该仅用您关心的字段替换
*
。下面是课程
表示搜索的上限和下限

然后,您可以使用
mysqli_num_rows()
(或基于您选择的DB库的类似方法)确定结果集中返回的行数

现在,您拥有了满足条件的行总数。然后,您可以轻松地计算结果范围内的4个随机数,并使用
mysqli_data_seek()
或类似工具直接转到随机偏移量处的记录,从中获取所需的值

总而言之:

$result = mysqli_query($db_conn, $sql); // here $sql is your SQL query
$num_records = 4; // your number of records to return
$num_rows = mysqli_num_rows($result);
$rows = array();

while ($i = 0; $i < $num_records; $i++) {
   $random_offset = rand(0, $num_rows - 1);
   mysqli_data_seek($result, $random_offset);
   $rows[] = mysqli_fetch_object($result);
}

mysqli_free_result($result);
$result=mysqli\u查询($db\u conn,$sql);//这里$sql是您的sql查询
$num_records=4;//要返回的记录数
$num_rows=mysqli_num_rows($result);
$rows=array();
而($i=0;$i<$num_记录;$i++){
$random\u offset=rand(0,$num\u行-1);
mysqli_data_seek($result,$random_offset);
$rows[]=mysqli_fetch_对象($result);
}
mysqli_免费_结果($result);

如果他能在max/min查询后确定4个点,那么他可以使用一个带有“WHERE points IN($rand_pt1,$rand_pt2,$rand_pt3,$rand_pt4)”的查询,如果有完整的点分布,这可能会起作用。然而,PHP中的随机化很可能会产生数据库中不存在的分数,例如,在
语法中使用
可能不会产生4个结果(甚至一个结果也没有)。这就是为什么我提供了
<。。。改为限制1
构造。非常感谢您的回答,但我还没有完全理解它。您正在选择最小点和最大点作为范围,但我需要一个范围,它取决于行计数器,我想,或者它也可以与点一起工作?例如,我希望在按点数排序时,在表的前10%中有4个随机行。在行数上随机是不可行的,除非它是一个永不删除的表(你可以基于该前提安全地使用AI PK)。至于任何一种随机化——不要在数据库中对这种表大小进行随机化,它最终会杀死你的MySQL。找到PHP端的替代方案,并接受它们可能不够精确的事实。@NielsKeurentjes再次感谢您的解释。我是MySQL的新手,但是您的查询将返回$myValue下面的第一行。在表中,许多行具有相同数量的点,每一行都有相同的被选中机会,这一点非常重要。对不起,我以前应该这么说。有没有办法解决这个问题?鉴于表的大小,这不是一个实用的解决方案,OP说“表包含800多万行,并且正在快速增长(目前每天150万行)”。当你的解决方案必须选择1亿行并从中随机选取一些结果时,你的解决方案也会让数据库屈服。非常感谢你的回答。两个问题。行数组中的所有行都是唯一的吗?第二,您认为是否可以/明智地使用最大值(点)而不是计数(*),并确定点的范围而不是行计数器。这将比您建议的快得多,但是如果我创建点范围,在某个范围内只有几行,而在其他范围内有更多的行,那么这些行将在随机函数中显示得更多。有没有办法解决这个问题,使所有行都有相同的更改?在我的问题中,我忘了说所有行都有相同的被选中的机会是非常重要的。