python - Django queryset:如何排除具有满足条件的任何相关对象的对象
问题描述
我在进行困难查询时偶然发现了 django 查询集的奇怪行为,我想知道是否有人知道如何改进此查询。
基本上我有一个这样的模型:
class Product(models.Model):
pass
class Stock(models.Model):
product_id = models.ForeignKey(Product)
date = models.DateField()
initial_stock = models.SmallIntegerField()
num_ordered = models.SmallIntegerField()
而且我想选择在任何日期都不可用的所有产品(意味着没有stock
与产品相关的initial_stock
字段大于num_ordered
字段的对象)。所以一开始,我做了:
Product.objects.exclude(stock__initial_stock__gt=F('stock__num_ordered')).distinct()
但我检查了一下,这个查询翻译为:
SELECT DISTINCT *
FROM "product"
LEFT OUTER JOIN "stock"
ON ("product"."id" = "stock"."product_id")
WHERE NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE (U1."initial_stock" > (U1."num_ordered") AND U1."id" = ("stock"."id"))
))
这会在股票上进行左连接,然后过滤掉initial_stock
大于的行,然后再将num_ordered
不同的行返回给我。
如您所见,当我有一个缺货的库存对象和另一个未缺货的库存对象时,它不起作用。
过滤后,我被排除在另一个日期实际可用的产品之外。
经过多次尝试,我认为这是可行的:
Product.objects.exclude(
stock__initial_stock__gt=F('stock__num_ordered')
).exclude(
stock__initial_stock__gt=F('stock__num_ordered')
).distinct()
因为它翻译为:
SELECT *
FROM "product"
LEFT OUTER JOIN "stock"
ON ("product"."id" = "stock"."product_id")
LEFT OUTER JOIN "stock" T3
ON ("product"."id" = T3."product_id")
WHERE NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE (U1."initial_stock" > (U1."num_ordered") AND U1."id" = ("stock"."id")) )
)) AND NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE U1."initial_stock" > (U1."num_ordered"))
))
这“有效”,但感觉就像一个奇怪的黑客,对于这么简单的事情似乎不是很有效。
你们中有人遇到过同样的问题并想出了更好的办法吗?
谢谢
编辑:感谢@dirkgroten的这个回答,为了比较,让我写下sql查询结果:
SELECT *,
EXISTS(
SELECT *
FROM "stock" U0
WHERE (U0."product_id" = ("product"."id") AND U0."initial_stock" > (U0."num_ordered"))
) AS "has_stock"
FROM "product"
WHERE EXISTS(
SELECT *
FROM "stock" U0
WHERE (U0."product_id" = ("product"."id") AND U0."initial_stock" > (U0."num_ordered"))
) = false
即使您的查询看起来更好,这两个查询似乎也具有相同的执行时间。虽然我很困惑为什么您的注释的以下过滤器不使用注释创建的列,而不是在 WHERE...
仍然关于我的回答,我不明白为什么在一种情况下有一个额外的过滤器,AND U1."id" = ("stock"."id"))
而在另一种情况下则没有。django 有可能在查询集 API 中有一个奇怪的行为吗?
解决方案
你最好使用 a Subquery
:
from django.db.models import OuterRef, Exists
in_stock = Stock.objects.filter(
product_id=OuterRef('pk'),
initial_stock__gt=F('num_ordered'))
out_of_stock_products = Product.objects.annotate(has_stock=Exists(in_stock))\
.filter(has_stock=False)
推荐阅读
- python - 如何通过 Selenium 设置 AngularJS 下拉菜单的值?
- python - While loop problem, or probably sth else?
- jquery - 通过不带 FormData 的 dropzone 上传文件
- reactjs - 在设置状态时使用带有 react-hook-form 无限循环的 React tinymce
- pandas - 在 pandas 中,根据 +- 的外观将列拆分为 2
- javascript - 光滑的滑块不会加载
- javascript - 从 React 中的主应用打开现有应用?
- ios - 如何避免Build react native ios错误代码65
- string - Kotlin 字符串日期格式化程序
- r - R导入excel表,单元格a1成为新列中的行值