【问题标题】:Django complex partitioning & ordering for tree Table树表的 Django 复杂分区和排序
【发布时间】:2021-02-13 23:23:00
【问题描述】:

我正在使用 Django 和 PostgreSQL 作为我的后端堆栈,该应用程序的主要功能是一个复杂的多级表,显示不同产品的带注释的时间序列数据。

所以基本上我定义了 2 个模型,一个是 Product,另一个是 Timestamp,而 Product Model 是分层的并使用 MPTT 用于实现树形结构的库。 每个产品可以有多个子级,最大 4 级深度,并有多个时间戳,描述它们在特定日期的表现数据。因此,我基本上是在为查询集中的每个产品注释所有时间序列数据,并且可以完美运行。我面临的问题是我需要通过注释值动态订购产品,而不破坏它们在中的位置>层次结构和/或父子关系。当我使用一些基本的 order_by("tree_id", "level" ...) 方法时,父子关系会被“覆盖”。另外重要是每个 Product-root 都有自己的 tree_id 并且是一棵单独的树

models.py

class Product(MPTTModel):
    owner = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True)
    budget = models.FloatField(null=True)
    creation_date = models.CharField(max_length=35, null=True)
    product_type = models.CharField(max_length=35, null=True)
    last_updated = models.DateTimeField(null=True)
    name = models.CharField(max_length=35, null=True)
    parent = parent = TreeForeignKey('self', on_delete=models.CASCADE,
                            null=True, blank=True)

class Timestamp(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True)
    viewed = models.IntegerField(default=0)
    bought = models.IntegerField(default=0)
    shown = models.IntegerField(default=0)
    date = models.DateField(null=True)

可以在下面看到一个非常基本的表格外观模型。

我当前的查询集排序功能无法正常工作,可以在此处查看

  filters = self.request.GET
  d_start, d_end = filters["date_start"], filters["date_end"]
  

  qs=Product._tree_manager.get_queryset_descendants(Product.objects.filter(
     Exists(Timestamp.objects.filter(
         date__gte=d_start,
         date__lte=d_end,
         product=OuterRef("pk"))
         ), level=0), include_self=True).annotate(
            viewed=(Sum("timestamp__viewed")),
            bought=(Sum("timestamp__bought")),
            shown=(Sum("timestamp__shown"))).order_by("tree_id", "level", "bought")

这个结果如下所示:

我怀疑该解决方案将需要一些 Raw SQL,可能需要将 Partitioning 与排序结合使用。

期待您的回答,并在此先感谢您。

【问题讨论】:

    标签: python sql django database postgresql


    【解决方案1】:

    postgreSQL 的递归 CTE 正是我所需要的。

    Django 的 ORM 非常可靠和健壮,但它也有其局限性。 复杂的、依赖于架构的功能(如递归查询)无法使用 ORM 实现 - 至少在我撰写此答案时不会。

    因此,这是一个需要我编写原始 SQL 的场景。 我不能在此处包含 SQL 代码,因为它包含合理的后端信息和逻辑。

    但是,如果您必须在 Django 中处理分层模型/查询,我可以告诉您一些事情。

    • 如果性能是重中之重,请勿使用 MpttTreeBeard 等库。它们都很慢,而且在我的场景中不适合生产使用,因为加载时间较长,用户体验会受到巨大影响。

    • 不要因为 Django 提供 ORM 而犹豫使用 Raw-SQL 查询。 迟早你会遇到 ORM 无法解决的问题,因为它的抽象级别很高

    • 不要浪费您宝贵的时间尝试重新发明轮子

    • 如果您出于任何原因必须使用关系数据库来解决这个问题,那么请使用 PostgreSQL,因为它提供了最佳性能,用于解决此问题的功能和扩展(我个人认为 PostgreSQL 是迄今为止最好的 SQL-DB。)

    • 为您的查询使用 ltree 扩展。它非常直观且易于理解,您可以在完整的 postgres-docs 中阅读它。

    • 如果您必须在不破坏层次结构的情况下对兄弟姐妹进行排序,请使用 Array 结合 ROW OVER

    当我从 Mptt 迁移到 CTEs 时,总体性能在我的场景中快了大约 10 倍。

    有用的链接:

    【讨论】:

    • 您能分享一下您的解决方案的代码吗?这将有助于未来的读者没有类似的问题。
    • @JeremyCaney 好吧,代码最终变成了一个 200 行长的 raw-sql 查询,其中包含有关数据库的相当敏感的数据。但我可以详细描述我是如何解决问题的,以及遇到问题时不该做什么。
    猜你喜欢
    • 1970-01-01
    • 2018-01-16
    • 2010-12-30
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多