You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中基于父模型过滤子模型多对多字段查询过慢求优化

问题:批量查询关联Parent模型的性能优化

我需要处理一批包含org_name、city、pincode的列表数据,查询对应的Parent模型的name字段(可能返回多个结果)。目前的实现代码处理20万条数据耗时约1分10秒,性能太差,求优化方案或新思路。


模型定义

class Parent(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=125, blank=True, null=True, db_index=True)


class RelatedModel(models.Model):
    org_name = models.CharField(max_length=125, blank=True, null=True, db_index=True)
    city = models.CharField(max_length=125, blank=True, null=True, db_index=True)
    pincode = models.CharField(max_length=125, blank=True, null=True, db_index=True)

    class Meta:
        unique_together = ('org_name', 'city', 'pincode')

class Child(models.Model):
    parent = models.OneToOneField(Parent, on_delete = models.CASCADE, related_name='child_parent')
    tags = models.ManyToManyField(RelatedModel, blank=True, related_name='tags_relatedmodel')

输入数据示例

data = [
    {"org_name":"Samaritan", "city":"Delhi", "pincode":"500800"},
    {"org_name":"Whitefield", "city":"Bengaluru"}
]

现有实现代码

query_fields = Q()
for i in data:
    if i.pincode == "" or i.pincode == None:
        query_fields = query_fields | Q(tags__org_name=i.org_name, tags__city=i.city)
    else:
        query_fields = query_fields | Q(tags__org_name=i.org_name, tags__city=i.city, tags__pincode=i.pincode)

queryset = Child.objects.prefetch_related('tags').filter(parent=OuterRef('id')
                ).filter(query_fields).order_by('parent_id').distinct('parent_id',).values('parent',)
        
result = Parent.objects.filter(id__in=Subquery(queryset)).order_by('-name')

优化思路与方案

1. 重构查询逻辑,减少子查询嵌套

原代码的多层子查询会导致数据库执行大量关联计算,可直接通过RelatedModel→Child→Parent的关联路径查询,避免嵌套开销:

from django.db.models import Q

# 构建RelatedModel的查询条件
related_q = Q()
for item in data:
    q = Q(org_name=item['org_name'], city=item['city'])
    if item.get('pincode') not in (None, ""):
        q &= Q(pincode=item['pincode'])
    related_q |= q

# 直接通过关联链查询Parent
matched_related = RelatedModel.objects.filter(related_q)
result = Parent.objects.filter(
    child_parent__tags__in=matched_related
).distinct().order_by('-name')

2. 添加复合索引优化查询效率

RelatedModel的单字段索引无法覆盖组合查询场景,建议添加针对性的复合索引:

class RelatedModel(models.Model):
    # 字段定义不变
    class Meta:
        unique_together = ('org_name', 'city', 'pincode')
        indexes = [
            models.Index(fields=['org_name', 'city', 'pincode']),  # 匹配含pincode的查询
            models.Index(fields=['org_name', 'city']),  # 匹配无pincode的查询
        ]

3. 预处理输入数据去重

如果输入数据存在重复的查询条件,先去重再构建查询,减少数据库需要处理的条件数量:

# 用元组作为键去重
unique_data = {}
for item in data:
    key = (item['org_name'], item['city'], item.get('pincode') or "")
    unique_data[key] = item
data = list(unique_data.values())

原代码中prefetch_related('tags')属于冗余操作,仅过滤时不需要加载关联数据,移除后可减少内存开销和查询步骤。

5. 用Exists子查询替代Subquery(可选)

如果仅需判断关联存在性,使用Exists比Subquery更高效:

from django.db.models import Exists, OuterRef

matched_related = RelatedModel.objects.filter(related_q)
child_exists = Child.objects.filter(
    parent=OuterRef('id'),
    tags__in=matched_related
).exists()

result = Parent.objects.filter(child_exists).order_by('-name')

内容的提问来源于stack exchange,提问作者goutham_mi3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 16:39:21