Django облегченное «обновление» огромного набора данных - PullRequest
0 голосов
/ 18 мая 2018

У меня очень большой набор данных, который мне нужно обновить как можно быстрее.Я делаю свои расчеты, и в конце у меня есть список вещей, которые необходимо обновить:

updates = [(instance_pk, value_to_update), (instance_pk, value_to_update), ..]

Модель одинакова во всем, как и столбец, который обновляется.

В прошлом я использовал Django Bulk Update - и я вполне уверен, что смог бы здесь - но даже это смехотворно пересилило (и поэтому делает слишком много обработки, потому что она обрабатывает полные экземпляры) для такой простой записи, что мне нужнослучилось быстроЯ упоминал, что здесь важна скорость?

Предоставляет ли Django что-нибудь , что может сделать это проще, без необходимости писать сырой SQL?

PostgreSQL 10 - это база данных, еслиэто оказывается актуальным.


С тех пор я тестировал сырой SQL, который предсказуемо эффективен, но вот как он выглядит, поэтому вы понимаете, что я пытаюсь сделать вDjango.

with connection.cursor() as c:
    c.executemany(
        'UPDATE app_model SET column_a = %s WHERE id = %s',
        [(value_to_update, instance_pk), ...]
    )

Может быть более эффективный SQL, чем выполнить много (что, я думаю, выдает много запросов в одной транзакции).

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

Пока Кос писал их превосходный ответ, я все еще работал над тем, как улучшить мой подход к raw-SQL, хотя бы немного ускорить процесс.

cursor.executemany() не очень умен.Он просто делает множество запросов одновременно.В PostsgreSQL есть предложение UPDATE FROM VALUES(..), но с ним сложно работать.К счастью, у psycopg2 есть специальное execute_values для создания этих наборов значений, они даже документируют пример обновления.

Если вы используете psycopg2 +, Postgres Django вернет вам исходное соединение psycopg2так что все это действительно легко использовать:

from django.db import connection
from psycopg2.extras import execute_values

with connection.cursor() as c:
    execute_values(
        c,
        'UPDATE app_model SET column_a = v.sid FROM (VALUES %s) as v (sid, bid) WHERE id = v.bid',
        [(value_to_update, instance_pk), ...]
    )

И это невероятно быстро.Я не думаю, что это станет лучше, чем это.

0 голосов
/ 18 мая 2018

Учитывая список пар ключ-значение, есть несколько способов использовать их для запуска ОБНОВЛЕНИЯ в SQL:

Просто много запросов

UPDATE app_model SET column_a = %s WHERE id = %s
UPDATE app_model SET column_a = %s WHERE id = %s
UPDATE app_model SET column_a = %s WHERE id = %s
UPDATE app_model SET column_a = %s WHERE id = %s

Это легкодля представления в Django:

for key, value in updates:
    Model.objects.filter(id=key).update(column_a=value)

Один запрос с кейсами

UPDATE app_model SET column_a = CASE
    WHEN id = %s THEN %s
    WHEN id = %s THEN %s
    WHEN id = %s THEN %s
    WHEN id = %s THEN %s
    ...
WHERE id IN (%s, %s, %s, %s, ...)

Предполагается, что это будет быстрее, потому что база данных может найти все строки более эффективно.В конечном итоге запрос будет очень длинным, и я бы предложил использовать этот подход с пакетами (скажем, 100 или 1000 строк, проведите эксперименты и посмотрите, что произойдет).

В Django вы можете сделать это либо черезORM :

Model.objects.filter(id__in=ids).update(
    column_a=Case(
        When(id=..., then=Value(...)),
        When(id=..., then=Value(...)),
        When(id=..., then=Value(...)),
        When(id=..., then=Value(...)),
        When(id=..., then=Value(...)),
        ...
    )
)

или через сторонний пакет django-bulk-update , который делает похожую вещь с более приятным API.

Один запрос с SELECT FROM

Примечание. Это специфично для PostgreSQL.Другие базы данных могут предлагать другие аналогичные расширения SQL.

Если вы можете создать таблицу data, содержащую все пары (key, value) для обновления, то у вас хорошее место для элегантного одиночного-запрос:

UPDATE app_model
SET column_a = data.value
FROM data
WHERE app_model.id = data.key;

Вместо таблицы вы также можете заменить подзапрос, если это проще.

В любом случае, я не видел способа построить UPDATE FROM запросов с использованием Django ORM пока нет, поэтому, насколько я могу судить, для этого требуется перейти на необработанный SQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...