【问题标题】:Conditional bulk updates: Django bulk_update or temporary table + sql条件批量更新:Django bulk_update 或临时表 + sql
【发布时间】:2020-11-08 19:11:08
【问题描述】:

感谢您阅读我的问题!我将 django (3.0.8) 与 postgres 12 一起使用。下面是 Inventory 的简化模型。大约有 100 万条记录。

class Inventory(models.Model):
    account = models.ForeignKey(Account, on_delete=models.PROTECT)
    item_id = models.LargeIntegerField(unique=True, db_index=True)
    amount = models.IntegerField()

每小时我们都会通过 REST API 收到一个帐户的新 snapshots,例如acc_0。它包含完整的项目列表(约 10 万条记录),而不是增量。我想应用 3 个操作:

  1. 如果item_id 不在新的snapshots 中,则设置amount=0 where account==acc_0
  2. 如果item_id 在新的snapshots 中设置amount=snapshot['amount'] where account==acc_0
  3. 如果snapshot['item_id'] 不在Inventory.item_id 中,则创建新项目

每次,大多数项目已经存在于 DB 中并且它们的数量没有变化,即真正的 delta 非常小(100-1k 记录)。

我现在做的似乎效率不高:

with transaction.atomic():
    new_items = {}
    update_items = {}
    Inventory.objects.filters(account=acc_0).update(amount=0)
    for snapshot in snapshots:
        item_id = snapshot['item_id']
        results = Inventory.objects.filter(item_id=item_id)
        if len(results) == 0:
            new_items[item_id] = Inventory(...)
        else:
            item = result[0]
            item.amount = snapshot['amount']
            update_items[item_id] = item
    Inventory.objects.bulk_create(new_items.values())
    Inventory.objects.bulk_update(update_items.values(), ['amount'])

我想知道我是否应该将快照上传到临时表并使用 UPDATE SET CASE JOIN、INSERT INTO SELECT NOT EXISTS 或者更好的是有一个更 Pythonic 的方式。

有一个类似的问题:Django Mass Update/Insert Performance 但它也是开放的。

【问题讨论】:

    标签: python sql django postgresql join


    【解决方案1】:

    由于您有三个问题,我将分三个部分回答您的问题。我不是 Postgres 方面的专家,但我可以告诉你使用 Django 解决问题的最方便方法。

    我不会说效率很高,因为我没有处理过大型数据集,但鉴于它们都是 Django 的默认函数,我希望它们表现得相当好。

    1 和 2:我假设您的 account 对象已经按照它们的 id 进行了排序。如果它们被排序,一个二分搜索算法就可以解决问题(在最坏的情况下仅等于线性搜索,最好在 log(N) 中,其中 N 是线性搜索的时间)。找到物品后,做任何你需要做的事情。如果它们没有排序,请不要打扰自己,只需进行标准线性搜索

    3:要么获取对象,要么创建对象。 Django 的get_or_create 正是你所需要的。

    【讨论】:

    • 是的,account 已编入索引。 Inventory.objects.filters().update() and bulk_*() 还不错。最慢的部分似乎是:for snapshot in snapshots: results = Inventory.objects.filter(item_id=item_id)
    • 第二行的snapshot在哪里?
    • 每小时从 REST API 检索一次。
    • 首先,你可能要考虑使用python的列表推导,它优化了速度,所以它看起来像results = [Inventory.objects.filter(item_id=snapshot['item_id']) for snapsnot in snapshots]。此外,我怀疑 if-else 语句是否应该属于 for 外观
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 2015-01-18
    • 1970-01-01
    • 2014-07-25
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多