【问题标题】:Having issues with optimizing query on foreign key在优化外键查询时遇到问题
【发布时间】:2016-09-20 20:29:58
【问题描述】:

我尝试了不同的方法来查询我的数据,但仍然会导致大量 ping 到数据库。

我尝试过使用select_related

这是我的模型:

class Order(models.Model):
    num = models.CharField(max_length=50, unique=True)

class OrderInfo(models.Model):
    info = models.CharField(max_length=100, unique=True)
    datetime = models.DateTimeField(auto_now_add=True, blank=True)
    order_fk = models.ForeignKey(Order)

我想要达到的目标:

OrderInfo 有一堆与Order 相关的信息。

我想要的是能够从数据库中获取最新的OrderInfo,但我希望它是Order 独有的。独特的部分是我努力减少查询量的地方。

ois = OrderInfo.objects.order_by('-datetime').select_related('order_fk')

当我尝试过滤时,它正在对每个订单进行查询以检查查询的唯一性。

For instances:
_ = [oi.order_fk for oi in ois] # queries reach to 20k, takes too long.

另外,我只需要限制我收到的回复数量,但我需要先知道有多少独特的Orders 才能限制它。

任何人,都知道最小化这些查询的适当方法,否则我可能需要重组我的模型。

注意事项:

  • Django 1.7
  • Python 2.7
  • SQLite

【问题讨论】:

  • 您能否将 .query 添加到查询 (ois) 的末尾并提供结果?您可以在poorsql.com 之类的网站上发布结果,以避免此处的字符限制并提供格式。一般来说,这将在 Django 创建查询时发布查询,我们可以利用它来尝试查看究竟是什么导致它查询这么多。另外,您的数据库中有多少订单?您的数据库中有多少信息对象?
  • @tredzko ois 打印时是 1 个查询。 goo.gl/czqYru。当我执行 for 循环时,它每次都在执行该查询。大约有 4k Orders 和大约 35k OrderInfos。 PS:不要贴短链接,原链接太长了。
  • 好吧,要明确一点,您正在寻找最新的 OrderInfo 对象,不管 Order,但每个 Order 只查找一次,对吗?我不确定我能否通过设置模型的方式来帮助减少这种情况。
  • @tredzko 正确。我想要按最近的顺序排列,并且只允许 1 个订单。您对如何正确建模有什么建议吗?
  • 我回复了一个答案,请回复它让我知道它是否有效,或者它是否可以帮助您找到答案。

标签: python django sqlite


【解决方案1】:

每个订单有多少 OrderInfo 对象?如果它很小,使用prefetch_related 可能最简单,只需在python 中进行过滤:

class Order(models.Model):
    num = models.CharField(max_length=50, unique=True)
    @property
    def latest_order_info(self):
        return max(self.orderinfo_set.all(), key=attrgetter('datetime')

然后在你的应用程序代码中,你可以这样做:

orders = Order.objects.filter(...).prefetch_related('orderinfo_set')

这有点浪费,但根据我的经验,除非父模型有非常多的子模型,否则通常不会成为瓶颈。

【讨论】:

  • orders = Order.objects.filter(...).prefetch_related('orderinfo_set') 这实际上是为我做的。除了限制可用帮助的数量外,它还减少了必须执行的查询数量。
【解决方案2】:

基于this stack overflow answer to a similar question,我认为您只需将其拆分为两个查询,以希望减少您必须与数据库挂钩的数量。

由于您使用的是 SQLite,因此您无权访问 DISTINCT ON,因此不妨尝试以下操作:

from django.db.models import Q, Max
import operator

ois = OrderInfo.objects.values('order_id').annotate(max_datetime=Max('datetime'))
filters = reduce(operator.or_, [(Q(order_id=oi['order_id']) &
    Q(datetime=oi['max_datetime'])) for oi in ois])
filtered_ois = OrderInfo.objects.filter(filters)

解释这是做什么的(同样,这是基于链接堆栈溢出的答案):

  • 获取按其order_id 分组的OrderInfo 对象,并在具有order_id 的所有OrderInfo 对象中注释具有最大日期时间的对象。
  • 使用这些值在OrderInfo 对象上创建一个新过滤器。
  • OrderInfo 对象使用过滤器并返回它们。

【讨论】:

  • 我将 order_id 更改为 order_fk_id 以使其正常工作。但是,当我尝试打印出filtered_ois 时,我得到以下OperationalError: Expression tree is too large (maximum depth 1000) 。我目前正在尝试使用它来使其正常工作。
  • @DKJ 祝你好运。告诉我你发现了什么!
  • 它试图执行的 sql。它太大而无法处理。不知道如何缩短它。我已经尝试减少ois和OrderInfo.objects.filter(filters)的返回值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-02
  • 2020-08-26
  • 2019-03-26
  • 2022-01-09
  • 2012-10-31
相关资源
最近更新 更多