Django 1.11.8中的SubQuery非常慢 - 我可以加快速度吗?

时间:2018-01-08 15:28:39

标签: django postgresql

我有一些表在两个单独的Django模型中没有通过FK关系连接,我正在尝试SubQuery。数据如下所示:

# "master" data table - reflects real property ownership by humans
# there are often changes to property ownership
class OwnershipRecord(Model):
  parcel = CharField(max_length=10, unique=True)
  owner_name = ...
  other data fields ...

# Poor man's 'elastic search' or an index of sorts for OwnershipRecord
class Lead(Model):
  ownership_record = OneToOneField(OwnershipRecord)
  preforeclosure = BooleanField(default=False)
  aggregated data/booleans/etc...

# "descriptor" table for a property
# there are not often changes to a property's physical traits
ResidentialMasterRecord(Model):
  parcel = CharField(max_length=10, unique=True)  # These are the SAME as OwnershipRecord
  livablesqft = ...
  lotsqft = ...

所以我正在开发一个可以按平方英尺过滤Lead个对象的查询。 LeadOwnershipRecord相关,但与ResidentialMasterRecord没有任何关系 - 它作为“事实表”存在,类似于特定地址的一组坐标。< / p>

我认为SubQuery适用于这种情况,我可以引用parcelOwnershipRecord中的ResidentialMasterRecord来链接两个实时。

非常慢。这是我正在尝试的查询:

from django.db.models import OuterRef, SubQuery
from myapp.models import OwnershipRecord, Lead, ResidentialMasterRecord

RMR_SQ = ResidentialMasterRecord.objects \
           .filter(parcel=OuterRef("ownership_record__parcel"))
qs = Lead.objects.select_related('ownership_record') \
         .annotate(sqft=SubQuery(RMR_SQ.values("livablesqft")[:1])) \
         .filter(sqft__gte=1500)

我正在查看15-45分钟范围内的查询时间 - 但我最终会得到结果...... 有关如何在保持非外键密钥结构的同时加速这一事情的任何想法?

Django 1.11.8 PostgreSQL 9.5 Droplet w / 8GB RAM,4核

2 个答案:

答案 0 :(得分:1)

(1)即使底层数据库没有fkey关系,也可以在ORM中指定两个表相关。
(2)像@bma提到的那样,索引会对性能产生很大的影响。

但是,在这里,通常是我的策略 - 将子查询分解为两个单独的查询并将一些数据保存在内存中。

如,

def chunkify(rg, chunk_size=1000):
    while rg:
        yield rg[:chunk_size]
        rg = rg[chunk_size:]

min_sq_footage = 1500
master_records = ResidentialMasterRecords.objects.filter(sqft__gte=min_sq_footage)
parcels = list(master_records.values_list('parcel', flat=True))
for parcel_chunk in chunkify(parcels):
    qs = Lead.objects.select_related('ownership_record').filter(ownership_record__parcel__in=parcel_chunk)
    # do some work

答案 1 :(得分:1)

这个答案是@bma和@wholevinski的评论灵感的结果。

Django docs中所述,

  

在ForeignKey上自动创建数据库索引。

此子查询问题的关键是在JOIN字段上建立索引(就我的问题而言,又名:parcel)。这很简单,看起来像这样:

class OwnershipRecord(Model):
  parcel = CharField(max_length=10, unique=True,
                     db_index=True)
  owner_name = ...
  other data fields ...

ResidentialMasterRecord(Model):
  parcel = CharField(max_length=10, unique=True,
                     db_index=True)
  livablesqft = ...
  lotsqft = ...

这个docs很稀疏,但很容易实现。

  

<强> db_index

     

Field.db_index

     

如果 True ,将为此字段创建数据库索引。

结果:我的查询时间从约30分钟变为1.55秒。

>>> import timeit
>>> from django.db.models import OuterRef, Subquery
>>> from leads.models import Lead
>>> from ownership.models import OwnershipRecord
>>> from mcassessor.models import ResidentialMasterRecord
>>> rmr_sq = ResidentialMasterRecord.objects.filter(parcelid=OuterRef('ownership_record__parcel'))
>>> qs = Lead.objects.select_related('ownership_record').annotate(sqft=Subquery(rmr_sq.values("livablesqft")[:1])).filter(sqft__gte=1700)
>>> toc = timeit.default_timer()
... qs_list = list(qs)
... print(timeit.default_timer() - toc)
[Out] 1.55457401276
>>> len(qs_list)
[Out] 823