您如何有效地(以数据库独立的方式)从表中选择随机记录?

时间:2011-09-01 14:21:50

标签: sql ruby-on-rails activerecord

这似乎是一个非常简单的问题,但它并没有像我预期的那样平凡。

我有一个俱乐部有俱乐部成员,我想从俱乐部随机抽出两名成员。

使用RANDOM()

一种方法是使用随机排序:

club.members.find(:all, :order => 'RANDOM()').limit(2)

然而,对于SqLite(开发人员数据库)和Postgres(生产),这是不同的,因为在MySql中命令是RAND()

虽然我可以开始编写一些包装器,但我觉得它尚未完成并且似乎不是ActiveRecord的一部分这一事实告诉我一些东西,并且RANDOM可能不是正确的方法。

使用索引直接拉出商品

另一种方法是按顺序拉出集合,然后从中选择随机记录:

首先,我们需要生成一个与成员对应的两个唯一索引的序列:

all_indices = 1..club.members.count
two_rand_indices = all_indices.to_a.shuffle.slice(0,2)

这给出了一个数组,其中两个索引保证是唯一的和随机的。我们可以使用这些索引来提取我们的记录

@user1, @user2 = Club.members.values_at(*two_rand_indices)

什么是最好的方法?

虽然第二种方法看起来很不错,但我也觉得我可能会遗漏一些东西,可能会让一个简单的问题复杂化。我显然不是第一个解决这个问题的人,那么通过它的最佳,最有效的SQL途径是什么?

3 个答案:

答案 0 :(得分:1)

你的第一个方法的问题是它用一个不可索引的表达式对整个表进行排序,只需要两行。这不能很好地扩展。

第二种方法的问题类似,如果表中有10个 9 行,那么您将从to_a生成一个大数组。这需要大量的记忆和时间来改变它。

同样通过使用values_at,您不是假设每个主键值都有一行从1开始计数,没有间隙吗?你不应该假设。

我推荐的是:

  1. 计算表格中的行数。

    c = Club.members.count
    
  2. Pick two random numbers介于1和计数之间。

    r_a = 2.times.map{ 1+Random.rand(c) }
    
  3. 使用limit and offset查询您的表格 不要使用ORDER BY,只需依赖RDBMS的任意排序。

    for r in r_a
        row = Club.members.limit(1).offset(r)
    end
    
  4. 另见:

答案 1 :(得分:0)

MySQL中的Order By RAND()函数:

ORDER BY RAND() LIMIT 4

当上面是查询中的最后一个子句时,这将选择一个随机的4行。

答案 2 :(得分:0)

尝试使用randumb gem,它实现了你提到的第二种方法