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

Django多表关联查询仅执行单条SQL的问题求助

解决Django多表关联查询N+1问题的方案

针对你的情况,核心问题是未正确覆盖所有关联关系导致N+1查询,以及使用raw查询时错误访问模型关联属性触发额外查询,以下是具体解决方案:

一、优化ORM查询(推荐)

你的select_related缺少了嵌套关联和部分关联,导致访问os等关联数据时触发大量额外查询。假设你的模型关系如下:

  • SinfraHosts 关联 Domain(字段名domain)
  • SinfraHosts 关联 HostsFixDataScans(字段名last_fix_data_scan)
  • HostsFixDataScans 关联 Os(字段名os)
  • SinfraHosts 关联 CompaniesDepartments(字段名system_care_by_department、additional_software_care_by_department)
  • SinfraHosts 关联 Companies(字段名installation_by_company、care_by_company)

优化后的查询代码:

machines = SinfraHosts.objects.filter(offline=0) \
    .select_related(
        'domain',
        'last_fix_data_scan__os',  # 嵌套关联os表,避免单独查询
        'installation_by_company',
        'system_care_by_department',
        'care_by_company',
        'additional_software_care_by_department'  # 补充缺失的关联
    ) \
    .order_by('system_care_by_department__department_name')  # 对应SQL的排序字段

关键说明:

  • select_related('last_fix_data_scan__os') 会一次性JOIN hosts_fix_data_scans和os表,避免访问os数据时触发单独查询
  • 补充additional_software_care_by_department关联,覆盖SQL中对应的JOIN
  • 排序字段需对应模型的关联字段路径,而非SQL别名

二、使用values()直接返回字典(适合无需模型实例的场景)

如果不需要完整的模型实例,使用values()可以直接生成包含所有所需字段的单条SQL,返回字典列表,彻底避免N+1问题:

machines = SinfraHosts.objects.filter(offline=0) \
    .values(
        'id',
        'hostname',
        'description',
        'installation_by_company_id',
        'care_by_company_id',
        'system_care_by_department_id',
        'system_care_accounting_type_id',
        'domain__domain_name',
        'last_fix_data_scan__os__operatingsystem',
        'last_fix_data_scan__os__lsbdistcodename',
        'last_fix_data_scan__os__lsbdistrelease',
        'last_fix_data_scan__os__lsbmajdistrelease',
        'system_care_by_department__department_name',
        'additional_software_care_by_department__department_name',
        'installation_by_company__company_name',
        'installation_by_company__company_abrebreation'
    ) \
    .order_by('system_care_by_department__department_name')

三、修复raw查询的额外查询问题

使用raw()时,若访问模型的关联属性(如machine.system_care_by_department),Django会自动触发额外查询。解决方法是:

  1. 移除原始SQL中多余的JOIN companies_departments(重复JOIN会导致性能问题)
  2. 直接通过raw实例的别名字段访问数据,不要触发模型关联

修正后的raw查询代码:

sql = """
SELECT
    `hosts`.`id`,
    `hosts`.`hostname`,
    `domains`.`domain_name`,
    `hosts`.`description`,
    `hosts`.`installation_by_company_id`,
    `hosts`.`care_by_company_id`,
    `hosts`.`system_care_by_department_id`,
    `hosts`.`system_care_accounting_type_id`,
    `os`.`operatingsystem`,
    `os`.`lsbdistcodename`,
    `os`.`lsbdistrelease`,
    `os`.`lsbmajdistrelease`,
    `system_care_department`.`department_name` AS `system_care_department`,
    `additional_software_care_department`.`department_name` AS `additional_software_care_department`,
    `system_installation_company`.`company_name` AS `system_installation_company`,
    `system_installation_company`.`company_abrebreation` AS `system_installation_company_abrebreation`
FROM
    `hosts`
JOIN
    `domains` ON `hosts`.`domain_id` = `domains`.`id`
JOIN
    `hosts_fix_data_scans` ON `hosts_fix_data_scans`.`host_id` = `hosts`.`id`
JOIN
    `os` ON `os`.`id` = `hosts_fix_data_scans`.`os_id`
JOIN
    `companies_departments` AS `system_care_department` ON `system_care_department`.`id` = `hosts`.`system_care_by_department_id`
JOIN
    `companies_departments` AS `additional_software_care_department` ON `additional_software_care_department`.`id` = `hosts`.`additional_software_care_by_department_id`
JOIN
    `companies` AS `system_installation_company` ON `system_installation_company`.id = `hosts`.`installation_by_company_id`
WHERE
    `hosts`.`offline` = 0
ORDER BY `system_care_department`;
"""
machines = SinfraHosts.objects.raw(sql)

# 访问示例:直接使用别名字段
for machine in machines:
    print(machine.system_care_department)  # 不要用machine.system_care_by_department.department_name

内容的提问来源于stack exchange,提问作者Henri Schomäcker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 14:03:22