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

Mat*_*ick 29 python random django sql-order-by django-models

如何使用Django获得两个不同的随机记录?我已经看到了关于如何获得一个的问题,但我需要获得两个随机记录,它们必须不同.

Cry*_*ops 97

order_by('?')[:2]对于具有大量行的表,其他答案建议的解决方案实际上是一件非常糟糕的事情.它导致ORDER BY RAND()SQL查询.作为一个例子,这里是mysql如何处理(其他数据库的情况没有太大差别).想象一下,你的表有十亿行:

  1. 要完成ORDER BY RAND(),它需要一个RAND()列来排序.
  2. 为此,它需要一个新表(现有表没有这样的列).
  3. 为此,mysql使用新列创建一个新的临时表,并将现有的ONE BILLION ROWS OF DATA复制到其中.
  4. 当它这样做时,它会按照您的要求执行,并为每一行运行rand()以填充该值.是的,你已经指示mysql生成一个十亿随机数.这需要一段时间.:)
  5. 几小时/几天后,当它完成时,现在必须对它进行排序.是的,你已经指示mysql排序这个十亿行,最糟糕的情况下的表(最糟糕的情况是因为排序键是随机的).
  6. 几天/几周之后,当它完成时,它忠实地抓住你实际需要的两个微小的行并为你返回它们.不错的工作.;)

注意:只是为了一点额外的肉汁,请注意mysql最初会尝试在RAM中创建临时表.当它耗尽时,它会将所有内容都置于保持状态以将整个内容复制到磁盘上,因此您几乎可以在整个过程中获得额外的I/O瓶颈.

怀疑者应该查看生成的查询以确认它是ORDER BY RAND()Google的"按rand()排序"(带引号).

一个更好的解决方案是交换一个非常昂贵的查询三个便宜的(限制/偏移而不是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]
Run Code Online (Sandbox Code Playgroud)

  • +1非常好的解释和一个很好的例子!请记住大多数存储引擎(除了MySQL MyISAM之外什么都没有?)它必须遍历整个数据集以获取表的`count`,所以这也可能相当昂贵.一般来说,获取随机记录是一件相当昂贵的事情. (5认同)
  • 是的,我正在尝试获取1000个随机记录.这种方法很残酷.真是站不住脚. (2认同)
  • @Manganeez,我的意思是order_by('?')站不住脚,而不是这里的解决方案。不好意思,我不好。 (2认同)

Bar*_*tek 25

如果你在ORM中指定随机运算符我很确定它会给你两个截然不同的随机结果吗?

MyModel.objects.order_by('?')[:2] # 2 random results.
Run Code Online (Sandbox Code Playgroud)

  • 这可能会出现性能问题.有关详细信息,请参阅[Manganeez的答案](http://stackoverflow.com/questions/1731346/how-to-get-two-random-records-with-django#6405601) (5认同)

dni*_*t13 10

对于未来的读者.

获取所有记录的ID列表:

my_ids = MyModel.objects.values_list('id', flat=True)
my_ids = list(my_ids)
Run Code Online (Sandbox Code Playgroud)

然后从以上所有id中选择n个随机id:

n = 2
rand_ids = random.sample(my_ids, n)
Run Code Online (Sandbox Code Playgroud)

并获取这些ID的记录:

random_records = MyModel.objects.filter(id__in=rand_ids)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这个解决方案对我有用.只是注意,我必须做`my_ids = list(my_ids)`,否则我从`random.sample`得到一个`TypeError`.通过执行`sample_size = min(len(my_ids),10)`,我还实现了一种方法来处理记录比我的样本大小更少的`my_ids`. (2认同)

wso*_*son 6

Object.objects.order_by('?')[:2]

这将返回两个随机排序的记录.你可以加

distinct()
Run Code Online (Sandbox Code Playgroud)

如果数据集中有相同值的记录.