如何用Django获取两个随机记录

30 投票
7 回答
15642 浏览
提问于 2025-04-15 15:59

我想知道怎么用Django获取两个不同的随机记录。我看到过关于怎么获取一条记录的问题,但我需要获取两条随机记录,而且这两条记录必须不一样。

7 个回答

11

给未来的读者。

首先,获取所有记录的ID列表:

my_ids = MyModel.objects.values_list('id', flat=True)
my_ids = list(my_ids)

然后,从上面所有的ID中随机挑选n个ID:

n = 2
rand_ids = random.sample(my_ids, n)

最后,获取这些ID对应的记录:

random_records = MyModel.objects.filter(id__in=rand_ids)
106

其他回答中提到的 order_by('?')[:2] 这个方法,对于行数很多的表来说,其实是个非常糟糕的选择。它会导致生成一个 ORDER BY RAND() 的 SQL 查询。举个例子,假设你的表有十亿行数据:

  1. 为了实现 ORDER BY RAND(),数据库需要一个 RAND() 列来进行排序。
  2. 为了有这个列,它需要创建一个新表(因为现有的表没有这个列)。
  3. 接着,mysql 会创建一个新的临时表,并把现有的十亿行数据复制过去。
  4. 在复制的过程中,它会按照你的要求,为每一行运行 rand() 来生成随机数。没错,你让 mysql 生成了十亿个随机数。这可得花费一段时间。:)
  5. 几小时或几天后,当这个过程完成后,它还得对这些数据进行排序。是的,你让 mysql 对这十亿行数据进行排序,而排序的依据是随机数,这样的排序效率是最差的。
  6. 几天或几周后,当排序完成后,它才会把你真正需要的那两行数据提取出来并返回给你。真是辛苦了。;)

注意:为了让事情更复杂一点,mysql 最开始会尝试在内存中创建那个临时表。当内存不够用时,它会暂停操作,把所有数据复制到硬盘上,这样几乎整个过程都会遇到 I/O 瓶颈。

如果你对此有疑问,可以查看生成的查询,确认它是 ORDER BY RAND(),然后在谷歌上搜索 "order by rand()"(记得加上引号)。

一个更好的解决方案是,用三个便宜的查询来替代这个非常昂贵的查询(使用 limit/offset,而不是 ORDER BY RAND()):

import random
last = MyModel.objects.count() - 1

index1 = random.randint(0, last)
# Here's one simple way to keep even distribution for
# index2 while still gauranteeing not to match index1.
index2 = random.randint(0, last - 1)
if index2 == index1: index2 = last

# This syntax will generate "OFFSET=indexN LIMIT=1" queries
# so each returns a single record with no extraneous data.
MyObj1 = MyModel.objects.all()[index1]
MyObj2 = MyModel.objects.all()[index2]
26

如果你在ORM中指定了随机操作符,我很确定它会给你两个不同的随机结果,对吧?

MyModel.objects.order_by('?')[:2] # 2 random results.

撰写回答