将非 FK 条件添加到 Django ORM 中的 LEFT OUTER JOIN 以返回不连接的行

Posted

技术标签:

【中文标题】将非 FK 条件添加到 Django ORM 中的 LEFT OUTER JOIN 以返回不连接的行【英文标题】:Add a non-FK condition to a LEFT OUTER JOIN in Django ORM to return rows that don't join 【发布时间】:2015-11-19 19:39:18 【问题描述】:

我想运行以下类型的查询:

SELECT company.id
FROM company
LEFT OUTER JOIN employee
    ON company.id=employee.company_id AND employee.retired=True
WHERE employee.id IS NULL

IE,我希望数据库尝试将每个公司加入到退休=True 的员工,并将未能加入的公司返回给我。

这将给我所有没有退休员工的公司,包括那些没有员工的公司。

在 Django 中有什么方法可以在不使用 .raw() 的情况下做到这一点?

我可以使用 Company.objects.filter(employee__isnull=True) 进行 LEFT JOIN,但我无法添加 retired 加入条件,因此我会找到有退休员工的公司。我不能将其设为Q(),因为在WHERE 子句中,employee.retired 将为NULL。我能做到:

Company.objects.exclude(id__in=Employee.objects.filter(retired=True).values('company_id'))

相当于效率较低的:

SELECT * FROM company WHERE id NOT IN (SELECT company_id FROM employee WHERE retired=True)

有没有办法在 Django 中实现 LEFT JOIN?

或者至少将第二个查询更改为WHERE NOT EXISTS (SELECT 1 FROM employee WHERE company_id=company.id AND retired=True),以便我们过滤子查询?

我正在使用 Django 1.8.5、postgres 9.4 和 psycopg2 2.6.1

【问题讨论】:

您是使用 django 从头开始​​构建数据库,还是使用旧数据库?因为如果您使用的是 django,那么在id 中有一个 null 的员工是没有意义的。此外,最好发布您的模型,以便我们更好地了解您的数据库。 感谢尚,数据库是使用 Django 从头开始​​构建的,没有员工记录具有空 id - 这正是我检测到左连接没有设法找到满足连接的员工记录的方式标准。这些模型要复杂得多,所以我创建了这个简化的示例来解释我的要求。 所以Employee 模型有一个公司的外键,对吧? 是的,非常感谢,它有两个字段 - company_id 是公司的外键,retired 是布尔值。 SQL 查询和 Django ORM 代码都返回相同的行:所有没有退休员工的公司,包括根本没有员工的公司。只是第一次查询效率更高。 【参考方案1】:

好的,我不知道有更好的方法来一次性实现这一点,因为没有办法告诉 django 选择 ALL 员工没有退休。如果您认为您的查询可行,请使用Company.objects.raw()

这是我的尝试:

no_retired_employees_company = Employee.objects.filter(is_retired=False) \
                                               .values_list('company_id', flat=True).distinct()

no_employee_company = Company.objects.filter(employee__isnull=True) \
                                     .values_list('id', flat=True).distinct()

result = list(a) + list(b)

【讨论】:

不幸的是,这会返回有退休员工的公司。我知道你来自哪里 - 我试图将条件放入 WHERE 子句,但这是不可能的。 抱歉,我不清楚您的要求。您是否正在尝试退回没有退休员工的公司? 我想要所有没有退休员工的公司,包括根本没有员工的公司。 This post 是同样的问题。 谢谢,不幸的是,这会创建与我试图避免的示例相同的 SQL,即 SELECT * FROM Company WHERE id NOT IN (SELECT company_id FROM Employee WHERE retired=False)。如果 Employee 是 100K 行+,这将比LEFT JOIN 效率低得多。

以上是关于将非 FK 条件添加到 Django ORM 中的 LEFT OUTER JOIN 以返回不连接的行的主要内容,如果未能解决你的问题,请参考以下文章

Django ORM相关

Django:创建表单以添加相关数据

Django 使用 ORM 和条件 Where 子句连接表

Django ORM:未继承子级的字段和值。对象重复。 (使用Django管理界面)

Django ORM 查询

Django ORM 验证数据库中的一行