如何从MySQL表中随机选择满足某些条件的多行?

时间:2012-03-28 16:44:20

标签: mysql random rows

我正在寻找一种有效的方法,从MySQL表中随机选择满足某些条件的100行,可能有数百万行。

我发现的几乎所有内容都建议避免使用ORDER BY RAND(),因为性能和可伸缩性都很差。

然而,this article表明ORDER BY RAND()仍然可以用作一种“快速而快速的方式”。获取randow数据。

基于这篇文章,下面是一些示例代码,展示了我想要完成的任务。我的问题是:

  1. 这是从表格中随机选择100行(或最多数百行)的有效方式吗?

  2. 性能何时成为问题?

  3.     SELECT  user.* 
        FROM    (
                SELECT  id 
                FROM    user 
                WHERE   is_active = 1
                AND     deleted = 0
                AND     expiretime > '.time().'
                AND     id NOT IN (10, 13, 15)
                AND     id NOT IN (20, 30, 50)
                AND     id NOT IN (103, 140, 250)
            ORDER BY    RAND() 
                LIMIT   100
                ) 
                AS      random_users
        STRAIGHT JOIN   user
        ON      user.id = random_users.id
    

3 个答案:

答案 0 :(得分:1)

强烈建议您阅读此article。最后一段将涵盖多个随机行的选择。您应该能够注意到SELECT中将要描述的PROCEDURE语句。这将是您添加特定WHERE条件的地方。

ORDER BY RAND()的问题是此操作具有n*log2(n)的复杂性,而我链接的文章中描述的方法几乎具有复杂性。

让我们假设,使用ORDER BY RAND()从表中选择包含10个条目的随机行需要1 time unit

  entries  |  time units
-------------------------
       10  |         1     /* if this takes 0.001s */
      100  |        20
    1'000  |       300
   10'000  |     4'000
  100'000  |    50'000
1'000'000  |   600'000     /* then this will need 10 minutes */

你写道,你正在处理数百万的表。

答案 1 :(得分:0)

我担心没有人能够准确地回答你的问题。如果你真的想知道你需要对你的系统运行一些基准测试(理想情况下不是实时的,而是精确的副本)。针对不同的解决方案(例如使用PHP获取随机行)对这个解决方案进行基准测试,并将数字与您/您的客户认为“良好性能”的数据进行比较。然后提高数据量,以保持列值的分布接近真实你可以,看看性能开始下降的地方。说实话,如果它现在有一点空间,那么我会去做它。当(如果!)它成为瓶颈然后你可以看看它再次 - 或者只是在你的数据库中掏出额外的铁......

答案 2 :(得分:0)

尽可能预处理 尝试类似(类似VB的例子)

Dim sRND = New StringBuilder : Dim iRandom As New Random()
Dim iMaxID As Integer = **put you maxId here**
Dim Cnt as Integer=0
While Cnt < 100
      Dim RndVal As Integer = iRandom.Next(1, iMaxID)
      If Not ("10,13,15,20,30,50,103,140,250").Contains(RndVal) Then
            Cnt += 1
            sRND.Append("," & RndVal)
      end if
End While
String.Format("SELECT * FROM (Select ID FROM(User) WHERE(is_active = 1) AND deleted = 0 AND expiretime > {0} AND id IN ({1}) .blahblablah.... LIMIT 100",time(), Mid(sRND.ToString, 2))

我没有检查语法,但我希望你能得到我的漂移。 这将使MySql读取符合“IN”的记录,并在达到100时停止,而无需首先预处理所有记录。

如果你试试,请告诉我经过时间的差异。 (我很谨慎)