在你变得过于复杂之前,我建议你测试一下order_by('?') 在你的数据库上是否真的很慢。
在你的问题中,你说:
我知道用于随机查询的 django SQL 查询非常慢,所以我正在尝试编写自己的方法......
这是 Django documentation 所说的:
注意:order_by('?') 查询可能既昂贵又缓慢,具体取决于您使用的数据库后端。
所以你应该在这里检查你的数据库是否存在性能问题。
其他答案提出了两个选择:
- 在 0 和
count-1 之间选择随机数,然后根据这些 id 进行过滤。
- 选择数据库中所有 id 的列表,然后从列表中随机选择一些,并根据这些 id 进行过滤。
选项 1 存在边界条件和 ID 编号间隙的问题。
选项 2 进行两个数据库查询,其中一个返回数据库中的所有 ID 号。
这两个选项都可能返回按 id 编号排序的最终选择。
鉴于所有这些问题和日益增加的复杂性,您至少应该衡量收益以决定是否值得。
当我在 SQLite3 数据库中选择 1000 条小记录中的 10 条来测量选项 1 与 order_by('?') 的性能时,结果如下:
Select in database with random order:
205, 49, 28, 542, 428, 1, 337, 860, 374, 303
[8.38821005821228, 7.809916019439697, 7.193678855895996, 8.39355993270874, 8.132720947265625]
Filter by random id numbers:
135, 357, 406, 476, 552, 580, 662, 663, 670, 889
[8.62951397895813, 8.145615100860596, 8.251683950424194, 7.629027843475342, 7.384187936782837]
这是我在 PostgreSQL 中尝试相同操作时的结果:
Select in database with random order:
117, 337, 160, 500, 468, 178, 845, 542, 735, 525
[13.016371965408325, 12.65379810333252, 12.106752872467041, 12.485779047012329, 12.837188959121704]
Filter by random id numbers:
59, 65, 108, 161, 213, 246, 301, 813, 854, 969
[18.311591863632202, 20.5823872089386, 13.955725193023682, 13.034253120422363, 13.079485177993774]
如果有显着差异,order_by('?') 看起来更好。它在您的数据库中看起来如何?如果您决定使用选项 1,请更改边界以匹配您的身份证号码。如果您决定选择选项 2,其他答案看起来都不错。
这是我用来测试 SQLite3 版本的代码。您可以将其保存到文件并按原样运行:
# Tested with Django 1.9.2
import sys
import timeit
from random import sample
import django
from django.apps import apps
from django.apps.config import AppConfig
from django.conf import settings
from django.db import connections, models, DEFAULT_DB_ALIAS
from django.db.models.base import ModelBase
NAME = 'udjango'
SELECT_COUNT = 10
base_query = None
def select_in_database():
chosen = base_query.order_by('?')[:SELECT_COUNT]
return list(chosen)
def select_by_random_id():
db_size = base_query.count()
random_ids = sample(xrange(db_size), SELECT_COUNT)
chosen = base_query.filter(id__in=random_ids)
return list(chosen)
def main():
global base_query
setup()
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
syncdb(Person)
for i in range(1000):
Person.objects.create(first_name=str(i), last_name=str(i))
base_query = Person.objects.all()
print('Select in database with random order:')
print(', '.join(person.first_name for person in select_in_database()))
print(timeit.repeat('select_in_database()',
'from __main__ import select_in_database',
repeat=5,
number=10000))
print('Filter by random id numbers:')
print(', '.join(person.first_name for person in select_by_random_id()))
print(timeit.repeat('select_by_random_id()',
'from __main__ import select_by_random_id',
repeat=5,
number=10000))
def setup():
DB_FILE = NAME + '.db'
with open(DB_FILE, 'w'):
pass # wipe the database
settings.configure(
DEBUG=True,
DATABASES={
DEFAULT_DB_ALIAS: {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': DB_FILE}},
LOGGING={'version': 1,
'disable_existing_loggers': False,
'formatters': {
'debug': {
'format': '%(asctime)s[%(levelname)s]'
'%(name)s.%(funcName)s(): %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'}},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'debug'}},
'root': {
'handlers': ['console'],
'level': 'WARN'},
'loggers': {
"django.db": {"level": "WARN"}}})
app_config = AppConfig(NAME, sys.modules['__main__'])
apps.populate([app_config])
django.setup()
original_new_func = ModelBase.__new__
@staticmethod
def patched_new(cls, name, bases, attrs):
if 'Meta' not in attrs:
class Meta:
app_label = NAME
attrs['Meta'] = Meta
return original_new_func(cls, name, bases, attrs)
ModelBase.__new__ = patched_new
def syncdb(model):
""" Standard syncdb expects models to be in reliable locations.
Based on https://github.com/django/django/blob/1.9.3
/django/core/management/commands/migrate.py#L285
"""
connection = connections[DEFAULT_DB_ALIAS]
with connection.schema_editor() as editor:
editor.create_model(model)
main()