【发布时间】:2020-06-25 13:25:42
【问题描述】:
问题
我正在尝试使用 Django ORM 来执行相当于 SQL NOT IN 子句的操作,在子选择中提供 ID 列表以从日志记录表中返回一组记录。我不知道这是否可能。
模型
class JobLog(models.Model):
job_number = models.BigIntegerField(blank=True, null=True)
name = models.TextField(blank=True, null=True)
username = models.TextField(blank=True, null=True)
event = models.TextField(blank=True, null=True)
time = models.DateTimeField(blank=True, null=True)
我的尝试
我的第一次尝试是使用exclude,但这确实NOT 否定整个Subquery,而不是所需的NOT IN:
query = (
JobLog.objects.values(
"username", "job_number", "name", "time",
)
.filter(time__gte=start, time__lte=end, event="delivered")
.exclude(
job_number__in=models.Subquery(
JobLog.objects.values_list("job_number", flat=True).filter(
time__gte=start, time__lte=end, event="finished",
)
)
)
)
不幸的是,这会产生以下 SQL:
SELECT "view_job_log"."username", "view_job_log"."group", "view_job_log"."job_number", "view_job_log"."name", "view_job_log"."time"
FROM "view_job_log"
WHERE (
"view_job_log"."event" = 'delivered'
AND "view_job_log"."time" >= '2020-03-12T11:22:28.300590+00:00'::timestamptz
AND "view_job_log"."time" <= '2020-03-13T11:22:28.300600+00:00'::timestamptz
AND NOT (
"view_job_log"."job_number" IN (
SELECT U0."job_number"
FROM "view_job_log" U0
WHERE (
U0."event" = 'finished' AND U0."time" >= '2020-03-12T11:22:28.300590+00:00'::timestamptz
AND U0."time" <= '2020-03-13T11:22:28.300600+00:00'::timestamptz
)
)
AND "view_job_log"."job_number" IS NOT NULL
)
)
我需要的是第三个AND 子句是AND "view_job_log"."job_number" NOT IN 而不是AND NOT (。
我也尝试过先使用exclude 将子选择作为自己的查询进行,如下所示:
Django equivalent of SQL not in
但是,这会产生同样有问题的结果。然后我尝试了一个Q 对象,它产生了一个类似的查询:
query = (
JobLog.objects.values(
"username", "subscriber_code", "job_number", "name", "time",
)
.filter(
~models.Q(job_number__in=models.Subquery(
JobLog.objects.values_list("job_number", flat=True).filter(
time__gte=start, time__lte=end, event="finished",
)
)),
time__gte=start,
time__lte=end,
event="delivered",
)
)
对Q 对象的尝试再次产生以下SQL,但没有NOT IN:
SELECT "view_job_log"."username", "view_job_log"."group", "view_job_log"."job_number", "view_job_log"."name", "view_job_log"."time"
FROM "view_job_log" WHERE (
NOT (
"view_job_log"."job_number" IN (
SELECT U0."job_number"
FROM "view_job_log" U0
WHERE (
U0."event" = 'finished'
AND U0."time" >= '2020-03-12T11:33:28.098653+00:00'::timestamptz
AND U0."time" <= '2020-03-13T11:33:28.098678+00:00'::timestamptz
)
)
AND "view_job_log"."job_number" IS NOT NULL
)
AND "view_job_log"."event" = 'delivered'
AND "view_job_log"."time" >= '2020-03-12T11:33:28.098653+00:00'::timestamptz
AND "view_job_log"."time" <= '2020-03-13T11:33:28.098678+00:00'::timestamptz
)
有什么方法可以让 Django 的 ORM 做与AND job_number NOT IN (12345, 12346, 12347) 等效的事情吗?还是我必须使用原始 SQL 来完成此操作?
提前感谢您阅读整个文字墙问题。显式优于隐式。 :)
【问题讨论】:
-
您的模型缺少
event字段 -
@Lotram 啊,我已经从模型中删除了不相关的字段,并且有点过分热心。谢谢你的收获!
-
不确定这是否是预期的,但由于
JobLog.job_number可以为空,所以一旦您的子查询返回NULL(stackoverflow.com/questions/129077/…),您的NOT IN就不会通过。这正是 ORM 添加的"view_job_log"."job_number" IS NOT NULLSQL 在您执行exclude(field__in)时试图保护的内容,但如果您使用自己的notin查找,您将独自一人。
标签: python django postgresql django-models django-orm