Django中对两个Queryset执行Union时如何避免额外数据库查询?
Django Union查询次数与优化解答
1. 实际查询次数说明
正常生产环境下该流程仅会执行1次数据库查询:
Django的QuerySet本身是惰性执行的,只有当你主动触发求值操作(遍历、转列表、打印、调用len/count等方法)时才会真正发起数据库请求。你在PyCharm调试器中看到三个QuerySet都打印了数据,是调试器为了展示变量内容,主动触发了三个QuerySet的求值,属于调试环境的额外操作,和代码本身的运行逻辑无关。
如果你的代码本身没有对两个子QuerySet做任何触发求值的操作,那么Django会将global_tag查询 + user_tag查询 + union合并 + 排序整合成一条SQL,直接交给数据库执行,最终仅返回合并后的结果。
2. 仅执行最终合并查询的实现方案
只要遵循以下规则即可避免触发子QuerySet的额外查询:
- 构造子QuerySet时仅做筛选、字段过滤操作,不要进行任何会触发求值的操作:包括不要打印子QuerySet、不要遍历、不要用
list()包裹、不要调用len()/count()/first()等方法、不要取下标获取单条数据 - 排序操作统一放在union之后执行,不要单独给子QuerySet加排序规则,避免Django提前求值子查询
- 保证两个子QuerySet返回的字段数量、字段类型完全匹配,因为你的两个模型继承自同一个抽象类,天然满足该要求,无需额外处理
- 如果不需要对合并结果去重,可以给union方法加
all=True参数,查询性能更高
正确写法示例:
# 子QuerySet仅构造查询规则,不触发数据库请求 global_tag_qs = Global_Tag.objects.filter(valid=True) user_tag_qs = User_Tag.objects.filter(user=request.user, valid=True) # 执行union + 排序,此时才会发起1次数据库请求 merged_tags = global_tag_qs.union(user_tag_qs, all=True).order_by("name")
如果需要确认实际查询次数,可以在Django配置中开启SQL日志打印,生产环境不会出现调试器导致的额外查询。
内容的提问来源于stack exchange,提问作者CJT
相关产品推荐
相关产品推荐

