2014-11-20 68 views
9

我有数据库中需要更新peridocially数据。数据的来源将返回当前可用的所有数据,因此将包含尚未存在于数据库中的新数据。Django batching/bulk update_or_create?

当我循环访问源数据时,如果可能的话,我不想让1000个单独写入。

有什么比如update_or_create但是分批工作?

一个想法是将update_or_create与手动事务结合使用,但我不确定这是否将单个队列写入队列或将它整合到一个SQL插入中?

或者类似的可以使用@commit_on_success()的函数在update_or_create里面的循环工作?

除了翻译并将其保存到模型之外,我不对数据进行任何操作。没有什么是依赖于循环中存在的那个模型

+0

我认为在大多数sql服务器中没有更新或创建的单个查询。 [postgres 9.5]中有一个(https://wiki.postgresql.org/wiki/What's_new_in_PostgreSQL_9.5#INSERT_..._ON_CONFLICT_DO_NOTHING.2FUPDATE_.28.22UPSERT.22.29),但是django不支持它。 事务不会导致“单个”查询。它只会确保所有查询在失败时都会失败。事实上,它会减缓所有查询。 – imposeren 2015-07-13 07:38:15

+0

Upd。我对交易错了。对所有操作使用单个事务将加速您的写入。这对于postgres和sqlite至少是这样的:https://github.com/coderholic/django-cities/pull/85#issuecomment-125177370 – imposeren 2015-08-03 07:11:29

回答

1

批处理你的更新将是一个upsert命令,就像@imposeren所说的,Postgres 9.5为你提供了这个能力。我认为Mysql 5.7也可以(请参阅http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html),具体取决于您的具体需求。这就是说使用db光标最简单。没有什么不对,那是因为ORM不够用。

沿着这些线应该工作。这是伪代码,所以不要只是剪切粘贴这个,但这个概念是为了雅。这里

class GroupByChunk(object): 
    def __init__(self, size): 
     self.count = 0 
     self.size = size 
     self.toggle = False 

    def __call__(self, *args, **kwargs): 
     if self.count >= self.size: # Allows for size 0 
      self.toggle = not self.toggle 
      self.count = 0 
     self.count += 1 
     return self.toggle 

def batch_update(db_results, upsert_sql): 
    with transaction.atomic(): 
     cursor = connection.cursor() 
     for chunk in itertools.groupby(db_results, GroupByChunk(size=1000)): 
      cursor.execute_many(upsert_sql, chunk) 

假设是:

  • db_results某种结果的迭代器,无论是在列表或字典
  • db_results的结果,可以直接送入原SQL exec语句
  • 如果任何批量更新失败,您将回滚所有这些更新。如果你想将它移动到每个块,只需将with块向下推