SQLAlchemy 'bulk_save_objects' vs 'add_all' основная логическая разница? - PullRequest
0 голосов
/ 23 октября 2019

Учитывая следующие три метода использования sqlalchemy ORM для вставки объектов:

(1)

for obj in objects:
    session.add(obj)

(2)

session.add_all(objects)

(3)

session.bulk_save_objects(objects)

Предположим, длина objects[] равна 50000

  • Формирует ли метод (1) и отправляет 50000 вставки SQL-запросов?
  • Имеет ли метод(2) формировать и отправлять только 1 SQL-запрос?
  • Имеет ли метод (3) формировать и отправлять только 1 SQL-запрос?

Я знаю эти три метода сильно отличаются по скорости . Но в чем разница с базовыми деталями реализации ?

1 Ответ

2 голосов
/ 23 октября 2019

(2) в основном реализовано как (1), и оба могут выдавать 50 000 вставок во время сброса, если ORM должен извлечь сгенерированные значения, такие как первичные ключи. Они могут даже испускать больше, если эти 50 000 объектов имеют каскадные отношения.

In [4]: session.add_all([Foo() for _ in range(5)])

In [5]: session.commit()
BEGIN (implicit)
INSERT INTO foo DEFAULT VALUES RETURNING foo.id
{}
... (repeats 3 times)
INSERT INTO foo DEFAULT VALUES RETURNING foo.id
{}
COMMIT

Если вы заранее предоставите первичные ключи и другие значения, сгенерированные БД, тогда Session может объединять отдельные вставки в одно "executemany""операция, когда аргументы совпадают.

In [8]: session.add_all([Foo(id=i) for i in range(5)])

In [9]: session.commit()
BEGIN (implicit)
INSERT INTO foo (id) VALUES (%(id)s)
({'id': 0}, {'id': 1}, {'id': 2}, {'id': 3}, {'id': 4})
COMMIT

Если ваш драйвер DB-API реализует executemany() или эквивалент , используя метод, который позволяет ему выпустить одиноператор с несколькими данными, то это может привести к одному запросу. Например, после включения executemany_mode='values' журнал Postgresql содержит для вышеупомянутого

LOG: statement: INSERT INTO foo (id) VALUES (0),(1),(2),(3),(4)

Массовая операция пропускает большую часть механизма Session - такого как сохраняющиеся связанные объекты - в обмен наприрост производительности. Например, по умолчанию он не извлекает значения по умолчанию, такие как первичные ключи, что позволяет ему попытаться выполнить пакетные изменения с меньшим количеством операций executemany, в которых совпадают операция и аргументы.

In [12]: session.bulk_save_objects([Foo() for _ in range(5)])
BEGIN (implicit)
INSERT INTO foo DEFAULT VALUES
({}, {}, {}, {}, {})

In [13]: session.commit()
COMMIT

Может по-прежнему выдаватьнесколько операторов, опять же в зависимости от данных и используемого драйвера DB-API. Документация - хорошее чтение.

С включенными помощниками по быстрому исполнению psycopg2 вышеупомянутые результаты в журнале Postgresql

LOG: statement: INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES

Другими словаминесколько операторов были объединены в один оператор, отправляемый на сервер.

Итак, в конечном итоге ответ на все три вопроса - «это зависит», что, конечно, может показаться разочаровывающим.

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