2014-11-20 5 views
9

У меня есть данные в базе данных, которые необходимо обновлять перидоциально. Источник данных возвращает все, что доступно в этот момент времени, поэтому будет включать новые данные, которых еще нет в базе данных.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

+0

Обновление. Я ошибся в транзакциях. Использование единой транзакции для всех операций ускорит ваши записи. Это, по крайней мере, верно для postgres и sqlite: https://github.com/coderholic/django-cities/pull/85#issuecomment-125177370 – imposeren

ответ

1

Вычисление ваших обновлений будет командой upsert, и, как сказал @imposeren, Postgres 9.5 дает вам эту возможность. Я думаю, что Mysql 5.7 также (см. http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html) в зависимости от ваших конкретных потребностей. Это говорит, что, вероятно, проще всего использовать курсор db. Ничего плохого в этом нет, он есть, когда ORM просто недостаточно.

Что-то в этом направлении должно работать. Это psuedo-ish-код, поэтому не просто разрезайте-в-вставьте это, но концепция существует для ya.

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
  • Если какое-либо из пакетных обновлений терпит неудачу, вы будете откатывать ВСЕ из них. Если вы хотите переместить это для каждого фрагмента, просто нажмите на with блок вниз немного
Смежные вопросы