Подтвердить что ты не робот

SQLAlchemy: механизм, соединение и разность сеансов

Я использую SQLAlchemy и есть как минимум три объекта: engine, session и connection, которые имеют метод execute, поэтому, если я, например, хочу выбрать все записи из table я могу это сделать

engine.execute(select([table])).fetchall()

и это

connection.execute(select([table])).fetchall()

и даже это

session.execute(select([table])).fetchall()

- результаты будут одинаковыми.

Насколько я понимаю, если кто-то использует engine.execute он создает connection, открывает session (Alchemy позаботится об этом для вас) и выполняет запрос. Но существует ли глобальное различие между этими тремя способами выполнения такой задачи?

4b9b3361

Ответ 1

Однострочный обзор:

Поведение execute() во всех случаях одинаково, но это три разных метода в классах Engine, Connection и Session.

Что именно является execute() :

Чтобы понять поведение execute() нам нужно изучить класс Executable. Executable является суперклассом для всех типов "операторов", в том числе select(), delete(), update(), insert(), text() - в простейших словах, Executable является конструкцией выражения SQL, поддерживаемой в SQLAlchemy.

Во всех случаях метод execute() принимает текст SQL или построенное выражение SQL, то есть любое из множества конструкций выражений SQL, поддерживаемых в SQLAlchemy, и возвращает результаты запроса (ResultProxy - ResultProxy объект курсора DB-API для обеспечения более легкого доступа к строке столбцы.)


Прояснить это дальше (только для концептуальных разъяснений, а не для рекомендуемого подхода):

В дополнение к Engine.execute() (выполнение без установления соединения), Connection.execute() и Session.execute(), также можно использовать execute() непосредственно в любой Executable конструкции. Класс Executable имеет собственную реализацию execute() - согласно официальной документации, одно описание строки о том, что делает execute(), это " Скомпилировать и выполнить эту Executable ". В этом случае нам нужно явно привязать Executable (конструкцию выражения SQL) с объектом Connection или объектом Engine (который неявно получает объект Connection), поэтому execute() будет знать, где выполнить SQL.

Следующий пример демонстрирует это хорошо - учитывая таблицу, как показано ниже:

from sqlalchemy import MetaData, Table, Column, Integer

meta = MetaData()
users_table = Table('users', meta,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)))

Явное выполнение, т. Е. Connection.execute() - передача текста SQL или построенное выражение SQL для метода execute() Connection:

engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
    # ....
connection.close()

Явное выполнение без установления соединения ie Engine.execute() - передача текста SQL или построенное выражение SQL непосредственно к методу execute() Engine:

engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
    # ....
result.close()

Неявное выполнение, т.е. Executable.execute() также не требует установления соединения и вызывает метод execute() Executable, то есть он вызывает метод execute() непосредственно в конструкции выражения SQL (экземпляр Executable).

engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
    # ....
result.close()

Примечание. Указанный пример неявного исполнения с целью уточнения - этот способ выполнения настоятельно не рекомендуется - согласно документам:

"неявное исполнение" - очень старый шаблон использования, который в большинстве случаев более запутан, чем полезен, и его использование не рекомендуется. Обе модели, по-видимому, способствуют чрезмерному использованию "коротких сокращений" в дизайне приложений, которые впоследствии приводят к проблемам.


Ваши вопросы:

Насколько я понимаю, если кто-то использует engine.execute, он создает соединение, открывает сеанс (Alchemy заботится об этом для вас) и выполняет запрос.

Вы правы для части "если кто-то использует engine.execute он создает connection ", но не для "открывает session (Alchemy заботится об этом для вас) и выполняет запрос" - использование Engine.execute() и Connection.execute() (почти) одно и то же, в формальном, объект Connection создается неявно, а в более позднем случае мы явно его создаем. Что действительно происходит в этом случае:

'Engine' object (instantiated via 'create_engine()') -> 'Connection' object (instantiated via 'engine_instance.connect()') -> 'connection.execute({*SQL expression*})'

Но существует ли глобальная разница между этими тремя способами выполнения такой задачи?

На уровне БД это точно то же самое, все они выполняют SQL (текстовое выражение или различные конструкции выражения SQL). С точки зрения приложения есть два варианта:

  • Прямое выполнение - использование Engine.execute() или Connection.execute()
  • Использование sessions - эффективно обрабатывает транзакцию как единую единицу работы, с легкостью через session.add(), session.rollback(), session.commit(), session.close(). Это способ взаимодействия с БД в случае ORM, т.е. сопоставленных таблиц. Предоставляет identity_map для мгновенного получения уже доступных или вновь созданных/добавленных объектов в течение одного запроса.

Session.execute() конечном итоге использует метод выполнения инструкции Connection.execute() для выполнения инструкции SQL. Использование объекта Session - это метод SQLAlchemy ORM, рекомендованный для взаимодействия приложения с базой данных.

Выдержка из документов:

Важно отметить, что при использовании ORM SQLAlchemy эти объекты обычно не доступны; вместо этого объект Session используется как интерфейс к базе данных. Однако для приложений, которые основаны на прямом использовании текстовых операторов SQL и/или конструкций выражений SQL без участия служб управления более высокого уровня ORM, Engine и Connection являются королем (и королевой?) - читайте дальше.

Ответ 2

Набель ответ охватывает много деталей и полезен, но мне показалось странным следовать. Поскольку в настоящее время это первый результат Google для этой проблемы, добавьте мое понимание этого для будущих людей, которые найдут этот вопрос:

Запуск .execute()

Как отмечают OP и Nabell Ahmed, при выполнении простого SELECT * FROM tablename, нет никакой разницы в предоставляемом результате.

Различия между этими тремя объектами становятся важными в зависимости от контекста, в котором используется оператор SELECT, или, чаще, когда вы хотите делать другие вещи, такие как INSERT, DELETE и т.д.

Когда использовать Engine, Connection, Session вообще

  • Механизм - это объект самого низкого уровня, используемый SQLAlchemy. Он поддерживает пул соединений, доступных для использования всякий раз, когда приложению необходимо связаться с базой данных. .execute() - это удобный метод, который сначала вызывает conn = engine.connect(close_with_result=True), а затем conn.execute(). Параметр close_with_result означает, что соединение закрывается автоматически. (Я немного перефразирую исходный код, но по сути верно). edit: Вот исходный код для engine.execute

    Вы можете использовать движок для выполнения необработанного SQL.

    result = engine.execute('SELECT * FROM tablename;')
    #what engine.execute() is doing under the hood
    conn = engine.connect(close_with_result=True)
    result = conn.execute('SELECT * FROM tablename;')
    
    #after you iterate over the results, the result and connection get closed
    for row in result:
        print(result['columnname']
    
    #or you can explicitly close the result, which also closes the connection
    result.close()
    

    Это описано в документации по базовому использованию.

  • Соединение - это (как мы видели выше) то, что фактически выполняет работу по выполнению SQL-запроса. Вы должны делать это всякий раз, когда вам нужен больший контроль над атрибутами соединения, когда оно закрывается и т.д. Например, очень важным примером импорта является Транзакция, которая позволяет вам решить, когда фиксировать ваши изменения. в базу данных. При обычном использовании изменения принимаются автоматически. Используя транзакции, вы можете (например) запустить несколько различных операторов SQL, и если с одной из них что-то пойдет не так, вы можете отменить все изменения сразу.

    connection = engine.connect()
    trans = connection.begin()
    try:
        connection.execute("INSERT INTO films VALUES ('Comedy', '82 minutes');")
        connection.execute("INSERT INTO datalog VALUES ('added a comedy');")
        trans.commit()
    except:
        trans.rollback()
        raise
    

    Это позволит вам отменить оба изменения в случае сбоя, например, если вы забыли создать таблицу данных.

    Поэтому, если вы выполняете необработанный код SQL и нуждаетесь в контроле, используйте соединения

  • Сессии используются для аспекта управления отношениями с объектами (ORM) в SQLAlchemy (на самом деле вы можете увидеть это по тому, как они импортированы: from sqlalchemy.orm import sessionmaker). Они используют соединения и транзакции внутри для запуска своих автоматически сгенерированных операторов SQL. .execute() - это вспомогательная функция, которая проходит через то, с чем связана сессия (обычно это механизм, но может быть соединением).

    Если вы используете функциональность ORM, используйте сессию; если вы выполняете только прямые SQL-запросы, не привязанные к объектам, вам, вероятно, лучше использовать прямые подключения.

Ответ 3

Вот пример запуска DCL (Язык управления данными), такой как GRANT

def grantAccess(db, tb, user):
  import sqlalchemy as SA
  import psycopg2

  url = "{d}+{driver}://{u}:{p}@{h}:{port}/{db}".\
            format(d="redshift",
            driver='psycopg2',
            u=username,
            p=password,
            h=host,
            port=port,
            db=db)
  engine = SA.create_engine(url)
  cnn = engine.connect()
  trans = cnn.begin()
  strSQL = "GRANT SELECT on table " + tb + " to " + user + " ;"
  try:
      cnn.execute(strSQL)
      trans.commit()
  except:
      trans.rollback()
      raise