【问题标题】:How to do this in one SQL or Django ORM command?如何在一个 SQL 或 Django ORM 命令中执行此操作?
【发布时间】:2017-05-22 02:09:08
【问题描述】:

我正在尝试编写一个依赖于两个 ForeignKeys 的查询。

有一个Product可以有很多Occurence对象(ForeignKey)。 Occurence 对象可以有许多 Scan 对象(ForeignKey 也是)。

现在我想根据它的 datetime 属性获取每次出现的对象最后一次扫描,如果它的属性 price 不是 Null(Price 是 OneToOneField)。

天真的方法:

product = Product.objects.first()
scans = []
for occ in product.occurences.all():
    scan = occ.scans.filter(price__isnull=False).orderby("-datetime").first()
    if scan:
        scans.append(scan)

>>> result is scans list

这种方法有很多缺点。一种产品可能有数百次出现和数百万次扫描,因此最好是数据库来完成这项工作。

所以我正在寻找可以做到这一点的查询。

Scan.objects.filter(occurence__product=product,price__isnull=False)

这将返回价格属性不为空的产品的所有扫描。我想我应该使用GroupBy,但我不知道如何使用。

【问题讨论】:

  • 为什么需要GROUP BY?您没有任何注释,并且您正在从Scan 中选择所有列,所以我看不出它会有什么影响。当你确实需要 GROUP BY 时,Django 会自动将它添加到查询中。
  • 我认为我应该按出现次数对扫描进行分组,然后从每个组中获取最新的扫描。也许我不需要 GROUP BY。你知道如何解决这个问题吗?

标签: sql django django-models orm django-filter


【解决方案1】:

我希望这是你的模型架构..

class Product(models.Model):
    # fields

class Occurence(models.Model):
    # some other fields
    product = models.ForeignKey(Product)


class Scan(models.Model):
    # a few other fields
    occurence = models.ForeignKey(Occurence)

如果是这种情况,你应该可以这样做

product = Product.objects.get(pk=product_id)
occurences = product.occurence_set.all()
scans = Occurence.scan_set.filter(occurence__in = occurences).order_by('-date_field')[:n]

如果价格为空,不清楚您正在检查哪个型号。您可以为该模型添加价格过滤器,用于出现次数或扫描...

order_by 按字段对数据进行排序。在 django 中,slicing([:n] 类似于 MySQL 中的 limit 或 SQL 中的 top)。

获取每次出现的最新元素

只有在 bulk_create 未插入数据/同时将数据插入到表中时,这才有效。

Occurence.scan_set.filter(occurence__in = occurences).annotate(latest_scan=Max('occurance__scan__date_field')).filter(date_field=F('latest_scan'))

理想情况下,它应该是时间字段,正如您所说,它会有很多扫描实例。

【讨论】:

  • 是的,模型架构不错。您的回答中有两个问题,第一个是扫描数可能非常高(数百万),因此我无法从数据库中全部选择它们。另一件事是我只想要最后一次扫描(最低日期时间)。
  • 我添加了 order_by 和切片以从查询中选择最新的扫描。如果需要添加任何其他内容,请告诉我。
  • 第一个问题是所有扫描都加载到内存中(我不能这样做),第二个问题是它没有返回正确的结果(它可以从一个返回 n 个最新扫描发生。
  • 1.它不会将所有扫描加载到内存中。 django将此查询转换为sql查询,并将此切片转换为sql的LIMIT。只需执行print(Occurence.scan_set.filter(occurence__in = occurences).order_by('-date_field')[:n].query),您将获得生成的 sql 查询。 2. 它不会只输出一次出现的扫描,因为我们在过滤出现后执行 order_by。我认为这是正确的方法之一。由于一切都在 SQL 中发生
  • 好的,但仍然存在一个问题,它可以返回occurrence1.somescanoccurrence1.somescan等。我想要occurrence1.lastscanoccurrence2.lastscan ...occurrence_n.lastscan
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-10
  • 2018-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-26
相关资源
最近更新 更多