Php 这种从数据库中随机选择记录的方法有什么缺陷吗?

Php 这种从数据库中随机选择记录的方法有什么缺陷吗?,php,mysql,performance,algorithm,Php,Mysql,Performance,Algorithm,我有一个数据库表,里面有大约30k条记录 我想一次随机选择一条记录(当用户要求时),从表中删除该记录,然后将其插入另一个表中 我听说/发现按兰德(RAND)(排序)可能会很慢。所以我使用这个算法(伪代码): 现在,有了30k条记录,我似乎很快就能得到随机ID。然而,随着表的大小减少到15k、10k、5k、100等(可能是几个月),我担心这可能会开始变慢 我可以做些什么来提高这种方法的效率吗?或者,我应该从哪个点开始执行orderbyrand(),而不是使用这种方法?(例如,当还剩5k行时,开始按

我有一个数据库表,里面有大约30k条记录

我想一次随机选择一条记录(当用户要求时),从表中删除该记录,然后将其插入另一个表中

我听说/发现按兰德(RAND)(排序)可能会很慢。所以我使用这个算法(伪代码):

现在,有了30k条记录,我似乎很快就能得到随机ID。然而,随着表的大小减少到15k、10k、5k、100等(可能是几个月),我担心这可能会开始变慢


我可以做些什么来提高这种方法的效率吗?或者,我应该从哪个点开始执行
orderbyrand()
,而不是使用这种方法?(例如,当还剩5k行时,开始按RAND()排序?)

您可以使用该方法获取随机ID,但不要检查它是否存在,而是尝试获取最近的ID

SELECT * FROM table WHERE id >= $randomId ORDER BY id LIMIT 0,1

如果失败,则选择较低的记录。

一种方法可能是确定记录数并按记录选择:

select floor(count(*) * rand()) from thetable;
在限制中使用生成的记录编号(例如,
chosenrec
):

select * from thetable limit chosenrec, 1;
我可以推荐一个单独的表格。要生成此项,请创建一个如下表:

CREATE TABLE Shuffle
(
    SequentialId INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    OtherTableId INT NOT NULL
)
值得注意的是,不必考虑外键约束。例如,在SQL Server中,我会说在DELETE CASCADE上添加一个带有
的外键约束;如果你有一个在MySQL中可以使用的存储引擎,那就试试吧

现在,用您选择的语言:

  • 获取另一个表中所有ID的数组(如@Truth在注释中所建议的)
  • 使用Fisher-Yates洗牌这些ID(需要线性时间)
  • 按顺序将它们插入洗牌表中

  • 现在,您有了一个随机顺序,因此您只需将
    内部联接
    Shuffle
    表,然后
    按Shuffle.SequentialId排序
    即可找到第一条记录。如果在处理随机数时无法在删除级联上执行
    ,操作,则可以手动从
    Shuffle
    中删除记录,通常最好不要重复。相反,尝试获取一个包含所有可能id的数组,并从中随机选择。如果可以保证id是连续的(这是罕见的,除非从未删除任何行…),则该数组的可能副本将是统一的。这将是有效的,但它不会以统一的方式选择id(如果这对OP很重要)。当ID之间的间距较大时,较大的ID被选择的概率较高。看一个极端的例子,在值1和100的表格中有两个ID。此方法将在99%的时间内选择ID 100(而不是像统一选择方法那样选择50%)。在这种情况下,
    限制0
    会做什么?我想可以通过在外部代码中生成一个运算符来平衡。类似于:
    operator=rand(0,1)?“>=”:“谢谢,这是一个非常优雅的解决方案。这将需要更多的资源。如果它是一个永久性的表,顺序将始终是相同的。如果是内存表,我看不出这样做的意义(问题的所有者用服务器端语言提出了相同的建议)。无论如何,在我看来,混合应用程序逻辑和数据库逻辑是不好的。让dbms来处理事情。@Catata更多的资源比什么?在这种情况下,如果随机顺序创建一次似乎没有问题。此解决方案仅对实际存在的记录进行排序,然后在删除另一个表中的相应记录时删除无序排列记录。如果问题是一次选择一条随机记录,并且同一条记录可以被多次选择,那么这种解决方案将是不合适的。您可以在MySQL中的
    limit
    表达式中执行子查询吗<代码>从表格限制中选择*(从表格中选择楼层(计数(*)*rand()),1@BlueRaja DannyPflughoeft:我不确定;实际上,我在发布答案之前就试过了,因为这会让解决方案变得更好。但是我没有想出正确的语法(如果允许的话)。
    CREATE TABLE Shuffle
    (
        SequentialId INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        OtherTableId INT NOT NULL
    )