Сделки и sqlalchemy

Я пытаюсь выяснить, как вставить много (порядка 100 КБ) записей в базу данных, используя SQLAlchemy в Python 3. Все указывает на использование транзакций. Тем не менее, я немного смущен тем, как это делается.

На некоторых страницах говорится, что вы получаете транзакцию из connection.begin(), в других местах говорится, что это session.begin(), а на этой странице здесь говорится, что это session.create_transaction(), которого не существует.

Вот что я пытаюсь сделать:

def addToTable(listOfRows):
    engine = create_engine('postgresql+pypostgresql:///%s' % db,echo = False)
    Session = sessionmaker(bind = engine)
    session = Session()
    table = myTable(engine,session)

    for row in listOfRows:
       table.add(row)
    table.flush() ### ideally there would be a counter and you flush after a couple of thousand records


class myTable:

    def __init__(self,engine,session):
       self.engine  = engine
       self.session = session
       self.transaction =createTransaction()# Create transaction code here

   def add(self,row):
       newRow = tableRow(row) ## This just creates a representation of a row in the DB
       self.transaction.add(newRow)
       self.transaction.flush()

   def flush(self):
       self.transaction.commit()

Ответ 1

Я настоятельно рекомендую вам сделать оба урока, прежде чем продолжить свое путешествие с помощью SQLAlchemy. Они действительно полезны и объясняют многие концепции. Впоследствии я предлагаю вам читать Использование сеанса, так как это далее объясняет, как этот сеанс подходит ко всему этому.

К вашей проблеме есть два решения: один использует ORM, а другой - с помощью Core. Первое проще, последнее быстрее. Сначала сделайте легкий путь. Транзакция используется только для переноса всех ваших операторов в одну операцию. То есть, если что-то не удастся, вы можете прервать все это и не остаться где-то посередине. Таким образом, вы, скорее всего, хотите совершить транзакцию, но она будет работать без нее. Вот самый быстрый способ:

with session.begin():
    session.add_all([tableRow(row) for row in listOfRows])

В зависимости от ваших данных SQLAlchemy может даже оптимизировать ваш оператор INSERT таким образом, чтобы он выполнял несколько за раз. Вот что происходит:

  • Сделка транзакций начинается с session.begin
  • Данные добавляются (используя add_all, но цикл с несколькими add также будет прав)
  • Сеанс завершен. Если что-то пойдет не так, транзакция будет прервана, и вы можете исправить ошибку.

Итак, это, безусловно, хороший способ, но это не самый быстрый способ, потому что SQLAlchemy должен пройти все алгоритмы ORM, которые могут привести к некоторым накладным расходам. Если это одноразовая инициализация базы данных, вы можете избежать ORM. В этом случае вместо создания класса ORM (tableRow) вы создаете словарь со всеми ключами (как зависит от данных). Снова вы можете использовать диспетчер контекстов:

with engine.begin() as connection:
    connection.execute(tableRow.__table__.insert().
                       values([row_to_dict(row) for row in listOfRows]))

Это, скорее всего, будет немного быстрее, но также менее удобным. Он работает так же, как и предыдущий сеанс, только строит инструкцию из Core, а не ORM.