Работа с двигателями и соединениями

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

Основное использование

Вспомним из Конфигурация двигателя, что Engine создается с помощью вызова create_engine():

engine = create_engine("mysql+mysqldb://scott:tiger@localhost/test")

Обычно create_engine() используется один раз для каждого конкретного URL базы данных, хранящегося глобально в течение всего времени жизни одного прикладного процесса. Один Engine управляет множеством отдельных соединений DBAPI от имени процесса и предназначен для одновременного обращения к нему. Engine является не синонимом функции DBAPI connect(), которая представляет только один ресурс соединения - Engine наиболее эффективен, когда создается только один раз на уровне модуля приложения, а не на каждый объект или вызов каждой функции.

Самая основная функция Engine заключается в предоставлении доступа к Connection, который затем может вызывать SQL-запросы. Вызов текстового оператора к базе данных выглядит следующим образом:

from sqlalchemy import text

with engine.connect() as connection:
    result = connection.execute(text("select username from users"))
    for row in result:
        print("username:", row.username)

Выше, метод Engine.connect() возвращает объект Connection, а при использовании его в менеджере контекста Python (например, оператор with:) метод Connection.close() автоматически вызывается в конце блока. Метод Connection представляет собой прокси объект для фактического DBAPI-соединения. DBAPI-соединение извлекается из пула соединений в тот момент, когда создается Connection.

Возвращаемый объект известен как CursorResult, который ссылается на курсор DBAPI и предоставляет методы для получения строк, аналогичные методам курсора DBAPI. Курсор DBAPI будет закрыт CursorResult, когда все его строки результатов (если таковые имеются) будут исчерпаны. Курсор CursorResult, не возвращающий никаких строк, например, оператор UPDATE (без возвращаемых строк), освобождает ресурсы курсора сразу же после создания.

Когда Connection закрывается в конце блока with:, ссылающееся DBAPI-соединение released передается в пул соединений. С точки зрения самой базы данных, пул соединений фактически не «закрывает» соединение, предполагая, что в пуле есть место для хранения этого соединения для следующего использования. Когда соединение возвращается в пул для повторного использования, механизм пула выполняет вызов rollback() на соединении DBAPI, чтобы все транзакционные состояния или блокировки были удалены (это известно как Сброс при возврате), и соединение готово к следующему использованию.

Наш пример выше иллюстрировал выполнение текстовой строки SQL, которая должна быть вызвана с помощью конструкции text(), чтобы указать, что мы хотим использовать текстовый SQL. Метод Connection.execute(), конечно, может выполнять и другие действия; учебное пособие см. в Работа с данными в Унифицированный учебник по SQLAlchemy.

Использование транзакций

Примечание

В этом разделе описывается, как использовать транзакции при работе непосредственно с объектами Engine и Connection. При использовании SQLAlchemy ORM публичный API для управления транзакциями осуществляется через объект Session, который внутренне использует объект Transaction. Дополнительную информацию см. в разделе Управление транзакциями.

Обязательства по мере выполнения

Объект Connection всегда выполняет SQL-запросы в контексте блока транзакции. При первом вызове метода Connection.execute() для выполнения SQL-запроса транзакция начинается автоматически, используя поведение, известное как autobegin. Транзакция остается на месте для области видимости объекта Connection до тех пор, пока не будут вызваны методы Connection.commit() или Connection.rollback(). После завершения транзакции объект Connection ожидает повторного вызова метода Connection.execute(), и в этот момент он снова автозапускается.

Такой стиль вызова называется commit as you go, и он показан на примере ниже:

with engine.connect() as connection:
    connection.execute(some_table.insert(), {"x": 7, "y": "this is some data"})
    connection.execute(
        some_other_table.insert(), {"q": 8, "p": "this is some more data"}
    )

    connection.commit()  # commit the transaction

В стиле «commit as you go» мы можем свободно обращаться к методам Connection.commit() и Connection.rollback() в непрерывной последовательности других утверждений, испускаемых с помощью Connection.execute(); каждый раз, когда транзакция завершается и испускается новое утверждение, неявно начинается новая транзакция:

with engine.connect() as connection:
    connection.execute("<some statement>")
    connection.commit()  # commits "some statement"

    # new transaction starts
    connection.execute("<some other statement>")
    connection.rollback()  # rolls back "some other statement"

    # new transaction starts
    connection.execute("<a third statement>")
    connection.commit()  # commits "a third statement"

Добавлено в версии 2.0: Стиль «commit as you go» - это новая особенность SQLAlchemy 2.0. Он также доступен в «переходном» режиме SQLAlchemy 1.4 при использовании механизма стиля «будущего».

Начать однажды

Объект Connection обеспечивает более явный стиль управления транзакциями, называемый begin once. В отличие от «commit as you go», «begin once» позволяет явно указать начальную точку транзакции, а сама транзакция может быть оформлена как блок контекстного менеджера, так что завершение транзакции будет неявным. Для использования «begin once» используется метод Connection.begin(), который возвращает объект Transaction, представляющий транзакцию DBAPI. Этот объект также поддерживает явное управление с помощью собственных методов Transaction.commit() и Transaction.rollback(), но в качестве предпочтительной практики также поддерживает интерфейс менеджера контекста, где он зафиксирует себя, когда блок завершится нормально, и выдаст откат, если возникнет исключение, перед распространением исключения наружу. Ниже показана форма блока «begin once»:

with engine.connect() as connection:
    with connection.begin():
        connection.execute(some_table.insert(), {"x": 7, "y": "this is some data"})
        connection.execute(
            some_other_table.insert(), {"q": 8, "p": "this is some more data"}
        )

    # transaction is committed

Подключение и запуск через раз от двигателя

Удобной сокращенной формой для приведенного выше блока «начать один раз» является использование метода Engine.begin() на уровне исходного объекта Engine, вместо выполнения двух отдельных шагов Engine.connect() и Connection.begin(); метод Engine.begin() возвращает специальный менеджер контекста, который внутренне поддерживает как менеджер контекста для Connection, так и менеджер контекста для Transaction, обычно возвращаемый методом Connection.begin():

with engine.begin() as connection:
    connection.execute(some_table.insert(), {"x": 7, "y": "this is some data"})
    connection.execute(
        some_other_table.insert(), {"q": 8, "p": "this is some more data"}
    )

# transaction is committed, and Connection is released to the connection
# pool

Совет

Внутри блока Engine.begin() мы можем вызвать методы Connection.commit() или Connection.rollback(), которые завершат транзакцию, обычно разграниченную блоком досрочно. Однако, если мы это сделаем, никакие дальнейшие SQL-операции не могут быть выполнены в блоке Connection до тех пор, пока блок не завершится:

>>> from sqlalchemy import create_engine
>>> e = create_engine("sqlite://", echo=True)
>>> with e.begin() as conn:
...     conn.commit()
...     conn.begin()
2021-11-08 09:49:07,517 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2021-11-08 09:49:07,517 INFO sqlalchemy.engine.Engine COMMIT
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: Can't operate on closed transaction inside
context manager.  Please complete the context manager before emitting
further commands.

Смешивание стилей

Стили «commit as you go» и «begin once» могут свободно смешиваться в одном блоке Engine.connect(), при условии, что вызов Connection.begin() не конфликтует с поведением «autobegin». Для этого Connection.begin() следует вызывать только до того, как были выполнены какие-либо SQL-запросы, или непосредственно после предыдущего вызова Connection.commit() или Connection.rollback():

with engine.connect() as connection:
    with connection.begin():
        # run statements in a "begin once" block
        connection.execute(some_table.insert(), {"x": 7, "y": "this is some data"})

    # transaction is committed

    # run a new statement outside of a block. The connection
    # autobegins
    connection.execute(
        some_other_table.insert(), {"q": 8, "p": "this is some more data"}
    )

    # commit explicitly
    connection.commit()

    # can use a "begin once" block here
    with connection.begin():
        # run more statements
        connection.execute(...)

При разработке кода, использующего «begin once», библиотека будет поднимать InvalidRequestError, если транзакция уже была «автозапущена».

Установка уровней изоляции транзакций, включая DBAPI Autocommit

Большинство DBAPI поддерживают концепцию настраиваемых уровней транзакций isolation. Традиционно это четыре уровня «READ UNCOMMITTED», «READ COMMITTED», «REPEATABLE READ» и «SERIALIZABLE». Они обычно применяются к соединению DBAPI перед началом новой транзакции, при этом следует отметить, что большинство DBAPI начинают транзакцию неявно при первом выполнении SQL-запросов.

DBAPI, поддерживающие уровни изоляции, обычно также поддерживают концепцию истинного «автокоммита», что означает, что само соединение DBAPI будет переведено в нетранзакционный режим автокоммита. Это обычно означает, что типичное поведение DBAPI, заключающееся в автоматической передаче «BEGIN» в базу данных, больше не происходит, но оно может включать и другие директивы. SQLAlchemy рассматривает концепцию «autocommit» как любой другой уровень изоляции; в том смысле, что это уровень изоляции, который теряет не только «read committed», но и теряет атомарность.

Совет

Важно отметить, как будет обсуждаться далее в разделе Понимание уровня изоляции автокоммита на уровне DBAPI, что уровень изоляции «autocommit», как и любой другой уровень изоляции, не влияет на «транзакционное» поведение объекта Connection, который продолжает вызывать методы DBAPI .commit() и .rollback() (они просто не имеют эффекта при autocommit), и для которого метод .begin() предполагает, что DBAPI начнет транзакцию неявно (это означает, что «begin» SQLAlchemy не изменяет режим autocommit).

Диалекты SQLAlchemy должны поддерживать эти уровни изоляции, а также автокоммит в максимально возможной степени.

Установка уровня изоляции или автокоммита DBAPI для соединения

Для отдельного объекта Connection, полученного от Engine.connect(), уровень изоляции может быть установлен на время существования этого объекта Connection с помощью метода Connection.execution_options(). Параметр известен как Connection.execution_options.isolation_level, а значения представляют собой строки, которые обычно являются подмножеством следующих имен:

# possible values for Connection.execution_options(isolation_level="<value>")

"AUTOCOMMIT"
"READ COMMITTED"
"READ UNCOMMITTED"
"REPEATABLE READ"
"SERIALIZABLE"

Не каждый DBAPI поддерживает все значения; если для определенного бэкенда используется неподдерживаемое значение, выдается ошибка.

Например, чтобы заставить REPEATABLE READ на определенном соединении, начните транзакцию:

with engine.connect().execution_options(
    isolation_level="REPEATABLE READ"
) as connection:
    with connection.begin():
        connection.execute("<statement>")

Совет

Возвращаемым значением метода Connection.execution_options() является тот же объект Connection, на котором был вызван метод, то есть он изменяет состояние объекта Connection на месте. Это новое поведение, начиная с версии SQLAlchemy 2.0. Это поведение не относится к методу Engine.execution_options(); этот метод по-прежнему возвращает копию Engine и, как описано ниже, может использоваться для создания нескольких объектов Engine с различными вариантами выполнения, которые, тем не менее, используют один и тот же диалект и пул соединений.

Примечание

Параметр Connection.execution_options.isolation_level обязательно не применяется к опциям уровня оператора, таким как Executable.execution_options(), и будет отклонен, если установлен на этом уровне. Это связано с тем, что параметр должен быть установлен на DBAPI-соединении на основе каждой транзакции.

Настройка уровня изоляции или автокоммита DBAPI для движка

Параметр Connection.execution_options.isolation_level также может быть установлен на весь движок, что часто предпочтительнее. Этого можно добиться, передав параметр create_engine.isolation_level в create_engine():

from sqlalchemy import create_engine

eng = create_engine(
    "postgresql://scott:tiger@localhost/test", isolation_level="REPEATABLE READ"
)

С приведенной выше настройкой каждое новое соединение DBAPI в момент его создания будет настроено на использование уровня изоляции "REPEATABLE READ" для всех последующих операций.

Поддержание нескольких уровней изоляции для одного двигателя

Уровень изоляции также может быть установлен для каждого двигателя, с потенциально большим уровнем гибкости, используя либо параметр create_engine.execution_options для create_engine(), либо метод Engine.execution_options(), последний из которых создаст копию Engine, которая разделяет диалект и пул соединений оригинального двигателя, но имеет свои собственные настройки уровня изоляции для каждого соединения:

from sqlalchemy import create_engine

eng = create_engine(
    "postgresql+psycopg2://scott:tiger@localhost/test",
    execution_options={"isolation_level": "REPEATABLE READ"},
)

С приведенным выше параметром соединение DBAPI будет настроено на использование уровня изоляции "REPEATABLE READ" для каждой новой начатой транзакции; но соединение как объединенное будет возвращено к исходному уровню изоляции, который присутствовал при первом возникновении соединения. На уровне create_engine() конечный эффект не отличается от использования параметра create_engine.isolation_level.

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

from sqlalchemy import create_engine

eng = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")

autocommit_engine = eng.execution_options(isolation_level="AUTOCOMMIT")

Выше, метод Engine.execution_options() создает неглубокую копию исходного Engine. Оба eng и autocommit_engine используют один и тот же диалект и пул соединений. Однако режим «AUTOCOMMIT» будет установлен на соединениях, когда они будут получены от autocommit_engine.

Настройка уровня изоляции, независимо от того, какая она, безоговорочно возвращается, когда соединение возвращается в пул соединений.

Понимание уровня изоляции автокоммита на уровне DBAPI

В предыдущем разделе мы познакомились с понятием параметра Connection.execution_options.isolation_level и тем, как его можно использовать для установки уровней изоляции базы данных, включая «автокоммит» на уровне DBAPI, который рассматривается SQLAlchemy как еще один уровень изоляции транзакций. В этом разделе мы попытаемся прояснить последствия такого подхода.

Если бы мы хотели проверить объект Connection и использовать его в режиме «autocommit», мы бы поступили следующим образом:

with engine.connect() as connection:
    connection.execution_options(isolation_level="AUTOCOMMIT")
    connection.execute("<statement>")
    connection.execute("<statement>")

Выше показано обычное использование режима «DBAPI autocommit». Нет необходимости использовать такие методы, как Connection.begin() или Connection.commit(), поскольку все утверждения фиксируются в базе данных немедленно. Когда блок завершается, объект Connection возвращает уровень изоляции «autocommit», и соединение DBAPI освобождается в пул соединений, где обычно вызывается метод DBAPI connection.rollback(), но поскольку вышеприведенные утверждения уже были зафиксированы, этот откат не оказывает никакого влияния на состояние базы данных.

Важно отметить, что режим «autocommit» сохраняется даже при вызове метода Connection.begin(); DBAPI не будет передавать в базу данных ни BEGIN, ни COMMIT при вызове Connection.commit(). Такое использование также не является сценарием ошибки, поскольку ожидается, что уровень изоляции «autocommit» может быть применен к коду, который в противном случае был написан с учетом транзакционного контекста; «уровень изоляции», в конце концов, является конфигурационной деталью самой транзакции, как и любой другой уровень изоляции.

В приведенном ниже примере утверждения остаются автокоммитом независимо от блоков транзакций уровня SQLAlchemy:

with engine.connect() as connection:
    connection = connection.execution_options(isolation_level="AUTOCOMMIT")

    # this begin() does not affect the DBAPI connection, isolation stays at AUTOCOMMIT
    with connection.begin() as trans:
        connection.execute("<statement>")
        connection.execute("<statement>")

Когда мы запускаем блок, подобный приведенному выше, с включенным протоколированием, протоколирование попытается указать, что, хотя вызывается уровень DBAPI .commit(), он, вероятно, не будет иметь никакого эффекта из-за режима автокоммита:

INFO sqlalchemy.engine.Engine BEGIN (implicit)
...
INFO sqlalchemy.engine.Engine COMMIT using DBAPI connection.commit(), DBAPI should ignore due to autocommit mode

В то же время, даже если мы используем «DBAPI autocommit», транзакционная семантика SQLAlchemy, то есть, поведение Connection.begin() на языке Python, а также поведение «autobegin», остаются на месте, хотя они и не влияют на само DBAPI-соединение. Для примера, приведенный ниже код вызовет ошибку, так как Connection.begin() вызывается после того, как autobegin уже произошел:

with engine.connect() as connection:
    connection = connection.execution_options(isolation_level="AUTOCOMMIT")

    # "transaction" is autobegin (but has no effect due to autocommit)
    connection.execute("<statement>")

    # this will raise; "transaction" is already begun
    with connection.begin() as trans:
        connection.execute("<statement>")

Приведенный выше пример также демонстрирует ту же тему, что уровень изоляции «autocommit» является конфигурационной деталью основной транзакции базы данных и не зависит от поведения begin/commit объекта SQLAlchemy Connection. Режим «autocommit» никак не взаимодействует с Connection.begin(), а Connection не обращается к этому статусу при выполнении собственных изменений состояния транзакции (за исключением сообщения в журнале движка о том, что эти блоки на самом деле не фиксируются). Обоснованием такой конструкции является поддержание полностью последовательной схемы использования Connection, в которой режим DBAPI-автокоммита может быть изменен независимо без указания каких-либо изменений кода в других местах.

Переключение между уровнями изоляции

Настройки уровней изоляции, включая режим автокоммита, сбрасываются автоматически, когда соединение возвращается обратно в пул соединений. Поэтому желательно не пытаться переключать уровни изоляции на одном объекте Connection, так как это приводит к излишней многословности.

Чтобы проиллюстрировать использование «autocommit» в специальном режиме в рамках одной проверки Connection, параметр Connection.execution_options.isolation_level должен быть повторно применен с предыдущим уровнем изоляции. В предыдущем разделе была проиллюстрирована попытка вызвать Connection.begin(), чтобы начать транзакцию во время выполнения autocommit; мы можем переписать этот пример, чтобы действительно сделать это, сначала изменив уровень изоляции перед вызовом Connection.begin():

# if we wanted to flip autocommit on and off on a single connection/
# which... we usually don't.

with engine.connect() as connection:
    connection.execution_options(isolation_level="AUTOCOMMIT")

    # run statement(s) in autocommit mode
    connection.execute("<statement>")

    # "commit" the autobegun "transaction"
    connection.commit()

    # switch to default isolation level
    connection.execution_options(isolation_level=connection.default_isolation_level)

    # use a begin block
    with connection.begin() as trans:
        connection.execute("<statement>")

Выше, чтобы вручную изменить уровень изоляции, мы использовали Connection.default_isolation_level для восстановления уровня изоляции по умолчанию (предполагая, что это то, что мы хотим здесь). Однако, вероятно, лучше работать с архитектурой Connection, которая уже обрабатывает сброс уровня изоляции автоматически при регистрации. предпочтительный способ написать вышеописанное - использовать два блока

# use an autocommit block
with engine.connect().execution_options(isolation_level="AUTOCOMMIT") as connection:
    # run statement in autocommit mode
    connection.execute("<statement>")

# use a regular block
with engine.begin() as connection:
    connection.execute("<statement>")

Подведем итоги:

  1. Уровень изоляции «автокоммит на уровне DBAPI» полностью независим от понятия «начать» и «зафиксировать» объекта Connection.

  2. используйте отдельные проверки Connection для каждого уровня изоляции. Избегайте попыток переключения туда и обратно между «autocommit» при проверке одного соединения; позвольте движку выполнить работу по восстановлению уровней изоляции по умолчанию

Использование курсоров на стороне сервера (они же потоковые результаты)

Некоторые бэкенды имеют явную поддержку концепции «курсоров на стороне сервера» и «курсоров на стороне клиента». Курсор на стороне клиента означает, что драйвер базы данных полностью забирает все строки из набора результатов в память перед возвратом после выполнения оператора. Такие драйверы, как PostgreSQL и MySQL/MariaDB, обычно используют курсоры на стороне клиента по умолчанию. Курсор на стороне сервера, напротив, указывает на то, что строки результатов остаются в состоянии ожидания на сервере базы данных, пока строки результатов потребляются клиентом. Например, драйверы для Oracle обычно используют модель «на стороне сервера», а диалект SQLite, хотя и не использует настоящую архитектуру «клиент/сервер», все же использует подход небуферизованной выборки результатов, который оставляет строки результатов вне памяти процесса до того, как они будут потреблены.

Из этой базовой архитектуры следует, что «курсор на стороне сервера» более эффективен при получении очень больших наборов результатов, но в то же время может усложнить процесс взаимодействия клиента и сервера и быть менее эффективным для небольших наборов результатов (обычно менее 10000 строк).

Для тех диалектов, которые имеют условную поддержку буферизованных или небуферизованных результатов, обычно существуют ограничения на использование «небуферизованного» режима, или режима курсора на стороне сервера. При использовании диалекта psycopg2, например, ошибка возникает, если курсор на стороне сервера используется с любым типом оператора DML или DDL. При использовании драйверов MySQL с курсором на стороне сервера, соединение DBAPI находится в более хрупком состоянии и не восстанавливается так же изящно после ошибок, а также не позволяет выполнить откат до тех пор, пока курсор не будет полностью закрыт.

По этой причине диалекты SQLAlchemy всегда по умолчанию используют менее подверженную ошибкам версию курсора, что означает, что для диалектов PostgreSQL и MySQL по умолчанию используется буферизованный курсор «на стороне клиента», где полный набор результатов забирается в память до вызова любых методов выборки из курсора. Такой режим работы подходит в подавляющем большинстве случаев; небуферизированные курсоры, как правило, не полезны, за исключением редких случаев, когда приложение получает очень большое количество строк по частям, где обработка этих строк может быть завершена до получения большего количества строк.

Для драйверов баз данных, предоставляющих опции курсора на стороне клиента и сервера, опции выполнения Connection.execution_options.stream_results и Connection.execution_options.yield_per предоставляют доступ к «курсорам на стороне сервера» на основе каждого Connection или каждого запроса. Аналогичные опции существуют и при использовании ORM Session.

Потоковая передача с фиксированным буфером через yield_per

Поскольку отдельные операции выборки строк с полностью небуферизованными курсорами на стороне сервера обычно обходятся дороже, чем выборка сразу нескольких строк, параметр выполнения Connection.execution_options.yield_per настраивает Connection или оператор на использование доступных курсоров на стороне сервера, в то же время настраивая буфер строк фиксированного размера, который будет получать строки с сервера партиями по мере их потребления. Этот параметр может иметь целочисленное положительное значение при использовании метода Connection.execution_options() на Connection или на операторе при использовании метода Executable.execution_options().

Добавлено в версии 1.4.40: Connection.execution_options.yield_per как опция только для Core является новой в SQLAlchemy 1.4.40; для предыдущих версий 1.4 используйте Connection.execution_options.stream_results непосредственно в сочетании с Result.yield_per().

Использование этой опции эквивалентно ручной установке опции Connection.execution_options.stream_results, описанной в следующем разделе, а затем вызову метода Result.yield_per() на объекте Result с заданным целочисленным значением. В обоих случаях эффект, который дает эта комбинация, включает:

  • режим курсоров на стороне сервера выбирается для данного бэкенда, если он доступен и не является поведением по умолчанию для данного бэкенда

  • по мере получения строк результата, они будут буферизироваться партиями, где размер каждой партии до последней партии будет равен целочисленному аргументу, переданному опции Connection.execution_options.yield_per или методу Result.yield_per(); последняя партия затем буферизируется относительно оставшихся строк, меньших, чем этот размер

  • Размер раздела по умолчанию, используемый методом Result.partitions(), если он используется, также будет сделан равным этому целочисленному размеру.

Эти три вида поведения проиллюстрированы в примере ниже:

with engine.connect() as conn:
    with conn.execution_options(yield_per=100).execute(
        text("select * from table")
    ) as result:
        for partition in result.partitions():
            # partition is an iterable that will be at most 100 items
            for row in partition:
                print(f"{row}")

Приведенный выше пример иллюстрирует комбинацию yield_per=100 вместе с использованием метода Result.partitions() для запуска обработки строк в партиях, соответствующих размеру, получаемых с сервера. Использование Result.partitions() является необязательным, и если Result итерируется напрямую, новая партия строк будет буферизироваться для каждых 100 извлеченных строк. Вызов такого метода, как Result.all(), не должен использоваться, так как это приведет к полной выборке всех оставшихся строк сразу и нарушит цель использования yield_per.

Совет

Объект Result можно использовать в качестве менеджера контекста, как показано выше. При итерации с курсором на стороне сервера это лучший способ обеспечить закрытие объекта Result, даже если в процессе итерации будут возникать исключения.

Опция Connection.execution_options.yield_per переносится и в ORM, используется Session для получения объектов ORM, где она также ограничивает количество объектов ORM, создаваемых одновременно. Более подробную информацию об использовании Connection.execution_options.yield_per с ORM смотрите в разделе Получение больших наборов результатов с доходностью за - в Руководство по составлению запросов в ORM.

Добавлено в версии 1.4.40: Добавлено Connection.execution_options.yield_per в качестве опции выполнения на уровне ядра для удобной установки результатов потоковой обработки, размера буфера и размера раздела одновременно, что можно перенести на аналогичный случай использования ORM.

Потоковая передача с динамически растущим буфером с помощью stream_results

Чтобы включить курсоры на стороне сервера без определенного размера раздела, можно использовать опцию Connection.execution_options.stream_results, которая, как и Connection.execution_options.yield_per, может быть вызвана на объекте Connection или объекте statement.

Когда объект Result, переданный с помощью опции Connection.execution_options.stream_results, итерируется напрямую, строки извлекаются внутренне, используя схему буферизации по умолчанию, которая буферизирует сначала небольшой набор строк, затем все больший и больший буфер при каждом извлечении до предварительно настроенного предела в 1000 строк. Максимальный размер этого буфера может быть изменен с помощью опции выполнения Connection.execution_options.max_row_buffer:

with engine.connect() as conn:
    with conn.execution_options(stream_results=True, max_row_buffer=100).execute(
        text("select * from table")
    ) as result:
        for row in result:
            print(f"{row}")

Хотя опция Connection.execution_options.stream_results может быть совмещена с использованием метода Result.partitions(), в опцию Result.partitions() следует передать определенный размер раздела, чтобы не получить весь результат. Обычно проще использовать опцию Connection.execution_options.yield_per при настройке на использование метода Result.partitions().

Перевод имен схем

Для поддержки многопользовательских приложений, которые распределяют общие наборы таблиц по нескольким схемам, опция выполнения Connection.execution_options.schema_translate_map может быть использована для переназначения набора объектов Table для отображения под разными именами схем без каких-либо изменений.

Дана таблица:

user_table = Table(
    "user",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
)

Схема» этого Table, определенная атрибутом Table.schema, будет None. Атрибут Connection.execution_options.schema_translate_map может указать, что все объекты Table со схемой None будут вместо этого отображать схему как user_schema_one:

connection = engine.connect().execution_options(
    schema_translate_map={None: "user_schema_one"}
)

result = connection.execute(user_table.select())

Приведенный выше код вызовет SQL на базе данных формы:

SELECT user_schema_one.user.id, user_schema_one.user.name FROM
user_schema_one.user

То есть, имя схемы заменяется нашим переведенным именем. В карте может быть указано любое количество схем target->destination:

connection = engine.connect().execution_options(
    schema_translate_map={
        None: "user_schema_one",  # no schema name -> "user_schema_one"
        "special": "special_schema",  # schema="special" becomes "special_schema"
        "public": None,  # Table objects with schema="public" will render with no schema
    }
)

Параметр Connection.execution_options.schema_translate_map влияет на все конструкции DDL и SQL, созданные на языке выражений SQL, как получено из объектов Table или Sequence. Он не влияет на буквенные строки SQL, используемые через конструкцию text() или через простые строки, передаваемые в Connection.execute().

Эта функция действует только в тех случаях, когда имя схемы непосредственно выводится из имени схемы Table или Sequence; она не влияет на методы, в которых строковое имя схемы передается напрямую. По этой схеме, она действует в рамках проверок «может создавать» / «может уничтожать», выполняемых такими методами, как MetaData.create_all() или MetaData.drop_all(), и действует при использовании отражения таблицы, заданной объектом Table. Однако это не влияет на операции, выполняемые над объектом Inspector, поскольку имя схемы передается этим методам в явном виде.

Совет

Чтобы использовать функцию трансляции схемы с ORM Session, установите этот параметр на уровне Engine, затем передайте этот механизм в Session. В Session для каждой транзакции используется новый Connection:

schema_engine = engine.execution_options(schema_translate_map={...})

session = Session(schema_engine)

...

Предупреждение

При использовании ORM Session без расширений функция перевода схемы поддерживается только в виде одной карты перевода схемы на сессию. Она не будет работать, если разные карты перевода схемы задаются на основе каждого запроса, так как ORM Session не учитывает текущие значения перевода схемы для отдельных объектов.

Чтобы использовать один Session с несколькими конфигурациями schema_translate_map, можно использовать расширение Горизонтальное разделение. См. пример в Горизонтальное разделение.

Кэширование компиляции SQL

Добавлено в версии 1.4: SQLAlchemy теперь имеет прозрачную систему кэширования запросов, которая существенно снижает вычислительные затраты Python на преобразование конструкций SQL-операторов в SQL-строки как в Core, так и в ORM. См. введение на Прозрачное кэширование компиляции SQL добавлено ко всем операциям DQL, DML в ядре, ORM.

SQLAlchemy включает в себя комплексную систему кэширования для компилятора SQL, а также его ORM-вариантов. Эта система кэширования прозрачна в пределах Engine и обеспечивает, что процесс компиляции SQL для данного SQL-компилятора Core или ORM, а также связанные с ним вычисления, которые собирают механику выборки результатов для этого оператора, будут происходить только один раз для этого объекта оператора и всех других с идентичной структурой, в течение всего времени, пока конкретная структура остается в «скомпилированном кэше» движка. Под «объектами утверждений, имеющими идентичную структуру», обычно подразумевается SQL-оператор, который строится внутри функции и создается каждый раз, когда эта функция выполняется:

def run_my_statement(connection, parameter):
    stmt = select(table)
    stmt = stmt.where(table.c.col == parameter)
    stmt = stmt.order_by(table.c.id)
    return connection.execute(stmt)

Приведенный выше оператор сгенерирует SQL, похожий на SELECT id, col FROM table WHERE col = :col ORDER BY id, отмечая, что хотя значение parameter является обычным объектом Python, таким как строка или целое число, строковая SQL-форма оператора не включает это значение, поскольку использует связанные параметры. Последующие вызовы вышеуказанной функции run_my_statement() будут использовать кэшированную конструкцию компиляции в области видимости вызова connection.execute() для повышения производительности.

Примечание

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

Хотя SQLAlchemy имеет рудиментарный кэш операторов с ранних версий 1.x, а также расширение «Baked Query» для ORM, обе эти системы требовали высокой степени специального использования API для того, чтобы кэш был эффективным. Новый кэш в версии 1.4 является полностью автоматическим и не требует изменения стиля программирования для обеспечения эффективности.

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

Конфигурация

Сам кэш представляет собой объект типа словаря LRUCache, который является внутренним подклассом словаря SQLAlchemy, который отслеживает использование определенных ключей и имеет периодический шаг «обрезки», который удаляет наименее недавно использованные элементы, когда размер кэша достигает определенного порога. Размер этого кэша по умолчанию равен 500 и может быть настроен с помощью параметра create_engine.query_cache_size:

engine = create_engine(
    "postgresql+psycopg2://scott:tiger@localhost/test", query_cache_size=1200
)

Размер кэша может вырасти в 150% от заданного размера, прежде чем он будет сокращен до целевого размера. Таким образом, кэш размером 1200 может вырасти до 1800 элементов, после чего он будет обрезан до 1200.

Размер кэша основан на одной записи для каждого уникального SQL-запроса, созданного на каждом движке. SQL-запросы, генерируемые как ядром, так и ORM, обрабатываются одинаково. Операторы DDL обычно не кэшируются. Для того чтобы определить, что делает кэш, в журнале регистрации движка будут содержаться подробности о поведении кэша, описанные в следующем разделе.

Оценка производительности кэша с помощью протоколирования

Указанный выше размер кэша 1200 на самом деле довольно велик. Для небольших приложений, скорее всего, будет достаточно размера 100. Для оценки оптимального размера кэша, при условии наличия достаточного количества памяти на целевом узле, размер кэша должен быть основан на количестве уникальных строк SQL, которые могут быть отображены для используемого целевого механизма. Наиболее целесообразным способом увидеть это является использование эха SQL, которое наиболее непосредственно включается с помощью флага create_engine.echo или с помощью протоколирования Python; см. раздел Настройка ведения журнала о конфигурации протоколирования.

В качестве примера мы рассмотрим журнал, создаваемый следующей программой:

from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session

Base = declarative_base()


class A(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)
    data = Column(String)
    bs = relationship("B")


class B(Base):
    __tablename__ = "b"
    id = Column(Integer, primary_key=True)
    a_id = Column(ForeignKey("a.id"))
    data = Column(String)


e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)

s.add_all([A(bs=[B(), B(), B()]), A(bs=[B(), B(), B()]), A(bs=[B(), B(), B()])])
s.commit()

for a_rec in s.scalars(select(A)):
    print(a_rec.bs)

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

  • [raw sql] - драйвер или конечный пользователь выдал необработанный SQL, используя Connection.exec_driver_sql() - кэширование не применяется

  • [no key] - объект оператора является оператором DDL, который не кэшируется, или объект оператора содержит некэшируемые элементы, такие как определяемые пользователем конструкции или произвольно большие предложения VALUES.

  • [generated in Xs] - утверждение было пропуском кэша и должно было быть скомпилировано, а затем сохранено в кэше. на создание скомпилированной конструкции ушло X секунд. Число X будет в мелких долях секунды.

  • [cached since Xs ago] - утверждение было попаданием в кэш и не требовало перекомпиляции. Утверждение было сохранено в кэше с X секунд назад. Число X будет пропорционально длительности работы приложения и длительности кэширования утверждения, например, 86400 для 24-часового периода.

Каждый бейдж более подробно описан ниже.

Первые утверждения, которые мы увидим в приведенной выше программе, будут диалектом SQLite, проверяющим существование таблиц «a» и «b»:

INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("a")
INFO sqlalchemy.engine.Engine [raw sql] ()
INFO sqlalchemy.engine.Engine PRAGMA main.table_info("b")
INFO sqlalchemy.engine.Engine [raw sql] ()

Для двух вышеприведенных утверждений SQLite PRAGMA значок читается как [raw sql], что указывает на то, что драйвер отправляет строку Python непосредственно в базу данных с помощью Connection.exec_driver_sql(). Кэширование не применяется к таким утверждениям, поскольку они уже существуют в строковой форме, и ничего не известно о том, какие типы строк результатов будут возвращены, поскольку SQLAlchemy не разбирает строки SQL заранее.

Следующие утверждения, которые мы видим, это утверждения CREATE TABLE:

INFO sqlalchemy.engine.Engine
CREATE TABLE a (
  id INTEGER NOT NULL,
  data VARCHAR,
  PRIMARY KEY (id)
)

INFO sqlalchemy.engine.Engine [no key 0.00007s] ()
INFO sqlalchemy.engine.Engine
CREATE TABLE b (
  id INTEGER NOT NULL,
  a_id INTEGER,
  data VARCHAR,
  PRIMARY KEY (id),
  FOREIGN KEY(a_id) REFERENCES a (id)
)

INFO sqlalchemy.engine.Engine [no key 0.00006s] ()

Для каждого из этих утверждений значок читается как [no key 0.00006s]. Это указывает на то, что в этих двух конкретных утверждениях кэширование не произошло, поскольку DDL-ориентированная конструкция CreateTable не создала ключ кэша. Конструкции DDL обычно не участвуют в кэшировании, потому что они, как правило, не повторяются во второй раз, а DDL также является этапом конфигурирования базы данных, где производительность не так критична.

Значок [no key] важен еще по одной причине, поскольку он может быть получен для SQL-запросов, которые кэшируются, за исключением некоторых конкретных подконструкций, которые в настоящее время не кэшируются. Примерами этого являются пользовательские элементы SQL, которые не определяют параметры кэширования, а также некоторые конструкции, которые генерируют произвольно длинные и невоспроизводимые строки SQL, основными примерами которых являются конструкция Values, а также при использовании «многозначных вставок» с помощью метода Insert.values().

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

INFO sqlalchemy.engine.Engine INSERT INTO a (data) VALUES (?)
INFO sqlalchemy.engine.Engine [generated in 0.00011s] (None,)
INFO sqlalchemy.engine.Engine INSERT INTO a (data) VALUES (?)
INFO sqlalchemy.engine.Engine [cached since 0.0003533s ago] (None,)
INFO sqlalchemy.engine.Engine INSERT INTO a (data) VALUES (?)
INFO sqlalchemy.engine.Engine [cached since 0.0005326s ago] (None,)
INFO sqlalchemy.engine.Engine INSERT INTO b (a_id, data) VALUES (?, ?)
INFO sqlalchemy.engine.Engine [generated in 0.00010s] (1, None)
INFO sqlalchemy.engine.Engine INSERT INTO b (a_id, data) VALUES (?, ?)
INFO sqlalchemy.engine.Engine [cached since 0.0003232s ago] (1, None)
INFO sqlalchemy.engine.Engine INSERT INTO b (a_id, data) VALUES (?, ?)
INFO sqlalchemy.engine.Engine [cached since 0.0004887s ago] (1, None)

Выше мы видим, по сути, две уникальные SQL-строки: "INSERT INTO a (data) VALUES (?)" и "INSERT INTO b (a_id, data) VALUES (?, ?)". Поскольку SQLAlchemy использует связанные параметры для всех буквальных значений, даже если эти утверждения повторяются много раз для разных объектов, поскольку параметры разделены, фактическая строка SQL остается неизменной.

Примечание

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

Значок кэширования, который мы видим для первого появления каждого из этих двух утверждений, - [generated in 0.00011s]. Это означает, что утверждение не было в кэше, было скомпилировано в строку за .00011 с и затем было кэшировано. Когда мы видим значок [generated], мы знаем, что это означает, что произошел промах в кэше. Этого следует ожидать при первом появлении определенного оператора. Однако, если наблюдается много новых значков [generated] для долго работающего приложения, которое обычно использует одну и ту же серию SQL-запросов снова и снова, это может быть признаком того, что параметр create_engine.query_cache_size слишком мал. Когда оператор, который был кэширован, затем удаляется из кэша из-за обрезки кэша LRU менее используемых элементов, при следующем использовании он отобразит значок [generated].

Значок кэширования, который мы затем видим для последующих вхождений каждого из этих двух утверждений, выглядит как [cached since 0.0003533s ago]. Это означает, что утверждение было найдено в кэше, и первоначально было помещено в кэш .0003533 секунды назад. Важно отметить, что хотя значки [generated] и [cached since] означают количество секунд, они означают разные вещи; в случае [generated] это число является приблизительным отсчетом времени, которое потребовалось для компиляции утверждения, и будет чрезвычайно малым. В случае [cached since] это общее время, в течение которого утверждение находилось в кэше. Для приложения, работающего в течение шести часов, это число может быть [cached since 21600 seconds ago], и это хорошо. Большое число «кэшировано с тех пор» является признаком того, что эти утверждения не подвергались промахам кэша в течение длительного времени. Утверждения, которые часто имеют низкое число «cached since», даже если приложение работает долгое время, могут указывать на то, что эти утверждения слишком часто подвергаются промахам кэша, и что значение create_engine.query_cache_size может быть увеличено.

Затем наша примерная программа выполняет несколько SELECT’ов, где мы видим ту же схему «сгенерировано», затем «кэшировано», для SELECT’а таблицы «a», а также для последующих ленивых загрузок таблицы «b»:

INFO sqlalchemy.engine.Engine SELECT a.id AS a_id, a.data AS a_data
FROM a
INFO sqlalchemy.engine.Engine [generated in 0.00009s] ()
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id
INFO sqlalchemy.engine.Engine [generated in 0.00010s] (1,)
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id
INFO sqlalchemy.engine.Engine [cached since 0.0005922s ago] (2,)
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id

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

Сколько памяти использует кэш?

В предыдущем разделе были описаны некоторые методы проверки того, нужно ли увеличить размер create_engine.query_cache_size. Как узнать, не слишком ли велик кэш? Причина, по которой мы можем захотеть установить create_engine.query_cache_size не выше определенного числа, заключается в том, что у нас есть приложение, которое может использовать очень большое количество различных запросов, например, приложение, которое строит запросы на лету из поискового UX, и мы не хотим, чтобы у нашего хоста закончилась память, если, например, за последние 24 часа было выполнено сто тысяч различных запросов, и все они были закешированы.

Очень трудно измерить, сколько памяти занимают структуры данных Python, однако, используя процесс измерения роста памяти через top по мере добавления в кэш последовательной серии из 250 новых утверждений, можно предположить, что умеренное утверждение Core занимает около 12K, в то время как небольшое утверждение ORM занимает около 20K, включая структуры выборки результатов, которые для ORM будут намного больше.

Отключение или использование альтернативного словаря для кэширования некоторых (или всех) утверждений

Используемый внутренний кэш известен как LRUCache, но в основном это просто словарь. Любой словарь можно использовать в качестве кэша для любой серии утверждений, используя опцию Connection.execution_options.compiled_cache в качестве опции выполнения. Опции выполнения могут быть установлены на операторе, на Engine или Connection, а также при использовании метода ORM Session.execute() для вызовов в стиле SQLAlchemy-2.0. Например, чтобы запустить серию SQL-запросов и кэшировать их в определенном словаре:

my_cache = {}
with engine.connect().execution_options(compiled_cache=my_cache) as conn:
    conn.execute(table.select())

SQLAlchemy ORM использует вышеупомянутую технику для хранения кэша каждого маппера в рамках процесса «промывки» единицы работы, который отделен от кэша по умолчанию, настроенного на Engine, а также для некоторых запросов загрузчика отношений.

Кэш также можно отключить с помощью этого аргумента, отправив значение None:

# disable caching for this connection
with engine.connect().execution_options(compiled_cache=None) as conn:
    conn.execute(table.select())

Кэширование для диалектов сторонних производителей

Функция кэширования требует, чтобы компилятор диалекта создавал SQL-строки, которые безопасно использовать повторно для многих вызовов операторов, учитывая определенный ключ кэша, привязанный к этой SQL-строке. Это означает, что любые буквальные значения в операторе, такие как значения LIMIT/OFFSET для SELECT, не могут быть жестко закодированы в схеме компиляции диалекта, поскольку скомпилированная строка не будет пригодна для повторного использования. SQLAlchemy поддерживает визуализацию связанных параметров с помощью метода BindParameter.render_literal_execute(), который может быть применен к существующим атрибутам Select._limit_clause и Select._offset_clause пользовательским компилятором, что проиллюстрировано далее в этом разделе.

Поскольку существует множество сторонних диалектов, многие из которых могут генерировать литеральные значения из SQL-операторов без использования новой функции «literal execute», SQLAlchemy в версии 1.4.5 добавила атрибут диалектов, известный как Dialect.supports_statement_cache. Этот атрибут проверяется во время выполнения на наличие непосредственно в классе конкретного диалекта, даже если он уже присутствует в суперклассе, так что даже сторонний диалект, который является подклассом существующего диалекта SQLAlchemy с возможностью кэширования, такого как sqlalchemy.dialects.postgresql.PGDialect, все равно должен явно включать этот атрибут, чтобы кэширование было включено. Атрибут должен только быть включен после того, как диалект был изменен по мере необходимости и протестирован на возможность повторного использования скомпилированных SQL-запросов с различными параметрами.

Для всех сторонних диалектов, которые не поддерживают этот атрибут, в журнале регистрации для такого диалекта будет указано dialect does not support caching.

Если диалект был протестирован на кэширование, и, в частности, компилятор SQL был обновлен, чтобы не отображать любой литерал LIMIT / OFFSET в строке SQL напрямую, авторы диалекта могут применять атрибут следующим образом:

from sqlalchemy.engine.default import DefaultDialect


class MyDialect(DefaultDialect):
    supports_statement_cache = True

Флаг должен применяться также ко всем подклассам диалекта:

class MyDBAPIForMyDialect(MyDialect):
    supports_statement_cache = True

Добавлено в версии 1.4.5: Добавлен атрибут Dialect.supports_statement_cache.

Типичный случай для модификации диалекта следующий.

Пример: Рендеринг LIMIT / OFFSET с параметрами после компиляции

В качестве примера, предположим, что диалект переопределяет метод SQLCompiler.limit_clause(), который создает предложение «LIMIT / OFFSET» для оператора SQL, например, так:

# pre 1.4 style code
def limit_clause(self, select, **kw):
    text = ""
    if select._limit is not None:
        text += " \n LIMIT %d" % (select._limit,)
    if select._offset is not None:
        text += " \n OFFSET %d" % (select._offset,)
    return text

Приведенная выше процедура выдает целочисленные значения Select._limit и Select._offset как буквальные целые числа, встроенные в оператор SQL. Это обычное требование для баз данных, которые не поддерживают использование связанного параметра в пунктах LIMIT/OFFSET оператора SELECT. Однако отображение целочисленного значения на начальном этапе компиляции напрямую несовместимо с кэшированием, поскольку целочисленные значения предела и смещения объекта Select не являются частью ключа кэша, поэтому многие операторы Select с различными значениями предела/смещения не будут отображаться с правильным значением.

Исправление приведенного выше кода заключается в перемещении литерального целого числа в средство SQLAlchemy post-compile, которое отображает литеральное целое число вне начальной стадии компиляции, а во время выполнения до отправки оператора в DBAPI. Доступ к нему осуществляется на этапе компиляции с помощью метода BindParameter.render_literal_execute() в сочетании с использованием атрибутов Select._limit_clause и Select._offset_clause, которые представляют LIMIT/OFFSET как полное SQL-выражение, следующим образом:

# 1.4 cache-compatible code
def limit_clause(self, select, **kw):
    text = ""

    limit_clause = select._limit_clause
    offset_clause = select._offset_clause

    if select._simple_int_clause(limit_clause):
        text += " \n LIMIT %s" % (
            self.process(limit_clause.render_literal_execute(), **kw)
        )
    elif limit_clause is not None:
        # assuming the DB doesn't support SQL expressions for LIMIT.
        # Otherwise render here normally
        raise exc.CompileError(
            "dialect 'mydialect' can only render simple integers for LIMIT"
        )
    if select._simple_int_clause(offset_clause):
        text += " \n OFFSET %s" % (
            self.process(offset_clause.render_literal_execute(), **kw)
        )
    elif offset_clause is not None:
        # assuming the DB doesn't support SQL expressions for OFFSET.
        # Otherwise render here normally
        raise exc.CompileError(
            "dialect 'mydialect' can only render simple integers for OFFSET"
        )

    return text

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

SELECT x FROM y
LIMIT __[POSTCOMPILE_param_1]
OFFSET __[POSTCOMPILE_param_2]

Там, где указано выше, индикаторы __[POSTCOMPILE_param_1] и __[POSTCOMPILE_param_2] будут заполнены соответствующими целочисленными значениями во время выполнения оператора, после того как строка SQL будет извлечена из кэша.

После внесения изменений, подобных вышеуказанным, флаг Dialect.supports_statement_cache должен быть установлен на True. Настоятельно рекомендуется, чтобы диалекты сторонних разработчиков использовали флаг dialect third party test suite, который гарантирует, что такие операции, как SELECT с LIMIT/OFFSET, будут правильно отображаться и кэшироваться.

Использование ламбдас для значительного увеличения скорости создания операторов

Deep Alchemy

Эта техника, как правило, не является необходимой, за исключением сценариев с очень высокой производительностью, и предназначена для опытных программистов Python. Несмотря на свою простоту, она включает в себя концепции метапрограммирования, которые не подходят для начинающих разработчиков Python. Подход лямбда может быть применен позднее к существующему коду с минимальными усилиями.

Функции Python, обычно выраженные в виде лямбд, могут использоваться для генерации SQL-выражений, которые кэшируются на основе расположения в коде Python самой лямбда-функции, а также закрывающих переменных внутри лямбды. Это делается для того, чтобы кэшировать не только строково-компилированную форму SQL-выражения, как это обычно происходит в SQLAlchemy, когда не используется система лямбд, но и сам состав SQL-выражения на языке Python, который также имеет некоторую степень накладных расходов на Python.

Функция лямбда-выражения SQL доступна в качестве функции повышения производительности, а также опционально используется в опции with_loader_criteria() ORM для предоставления общего фрагмента SQL.

Синопсис

Лямбда-выражения строятся с помощью функции lambda_stmt(), которая возвращает экземпляр StatementLambdaElement, который сам по себе является исполняемой конструкцией выражения. Дополнительные модификаторы и критерии добавляются к объекту с помощью оператора сложения Python + или альтернативного метода StatementLambdaElement.add_criteria(), который позволяет использовать больше вариантов.

Предполагается, что конструкция lambda_stmt() вызывается внутри объемлющей функции или метода, которые предполагается использовать много раз в приложении, так что последующие выполнения, кроме первого, могут воспользоваться преимуществами кэширования скомпилированного SQL. Когда лямбда строится внутри объемлющей функции в Python, она также должна иметь закрывающие переменные, которые важны для всего подхода:

from sqlalchemy import lambda_stmt


def run_my_statement(connection, parameter):
    stmt = lambda_stmt(lambda: select(table))
    stmt += lambda s: s.where(table.c.col == parameter)
    stmt += lambda s: s.order_by(table.c.id)

    return connection.execute(stmt)


with engine.connect() as conn:
    result = run_my_statement(some_connection, "some parameter")

Выше, три вызываемые функции lambda, которые используются для определения структуры оператора SELECT, вызываются ровно один раз, а полученная строка SQL кэшируется в кэше компиляции движка. С этого момента функция run_my_statement() может быть вызвана любое количество раз, а входящие в нее вызываемые переменные lambda не будут вызваны, а будут использоваться только как ключи кэша для получения уже скомпилированного SQL.

Примечание

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

Краткое руководство по ламбдам

Основное внимание в системе лямбда SQL уделяется тому, чтобы никогда не было несоответствия между ключом кэша, созданным для лямбды, и строкой SQL, которую она произведет. LambdaElement и связанные с ним объекты будут запускать и анализировать заданную лямбду, чтобы рассчитать, как она должна кэшироваться при каждом запуске, пытаясь обнаружить любые потенциальные проблемы. Основные рекомендации включают:

  • Поддерживается любой тип утверждений - хотя предполагается, что конструкции select() являются основным примером использования lambda_stmt(), утверждения DML, такие как insert() и update(), также могут быть использованы:

    def upd(id_, newname):
        stmt = lambda_stmt(lambda: users.update())
        stmt += lambda s: s.values(name=newname)
        stmt += lambda s: s.where(users.c.id == id_)
        return stmt
    
    
    with engine.begin() as conn:
        conn.execute(upd(7, "foo"))
  • Случаи прямого использования также поддерживаются - lambda_stmt() может полностью вмещать функциональность ORM и использоваться непосредственно с Session.execute():

    def select_user(session, name):
        stmt = lambda_stmt(lambda: select(User))
        stmt += lambda s: s.where(User.name == name)
    
        row = session.execute(stmt).first()
        return row
  • Связанные параметры учитываются автоматически - в отличие от предыдущей системы «запеченных запросов» SQLAlchemy, система лямбда SQL учитывает буквенные значения Python, которые становятся связанными параметрами SQL автоматически. Это означает, что даже если данная лямбда выполняется только один раз, значения, которые становятся связанными параметрами, извлекаются из закрытия лямбды при каждом выполнении:

    >>> def my_stmt(x, y):
    ...     stmt = lambda_stmt(lambda: select(func.max(x, y)))
    ...     return stmt
    >>> engine = create_engine("sqlite://", echo=True)
    >>> with engine.connect() as conn:
    ...     print(conn.scalar(my_stmt(5, 10)))
    ...     print(conn.scalar(my_stmt(12, 8)))
    {execsql}SELECT max(?, ?) AS max_1
    [generated in 0.00057s] (5, 10){stop}
    10
    {execsql}SELECT max(?, ?) AS max_1
    [cached since 0.002059s ago] (12, 8){stop}
    12

    Выше, StatementLambdaElement извлек значения x и y из закрытия лямбды, которая генерируется каждый раз, когда вызывается my_stmt(); они были подставлены в кэшированную конструкцию SQL в качестве значений параметров.

  • В идеале лямбда должна создавать идентичную структуру SQL во всех случаях - Избегайте использования условий или пользовательских callables внутри лямбд, которые могут заставить ее создавать различные SQL на основе входных данных; если функция может условно использовать два различных фрагмента SQL, используйте две отдельные лямбды:

    # **Don't** do this:
    
    
    def my_stmt(parameter, thing=False):
        stmt = lambda_stmt(lambda: select(table))
        stmt += (
            lambda s: s.where(table.c.x > parameter)
            if thing
            else s.where(table.c.y == parameter)
        )
        return stmt
    
    
    # **Do** do this:
    
    
    def my_stmt(parameter, thing=False):
        stmt = lambda_stmt(lambda: select(table))
        if thing:
            stmt += lambda s: s.where(table.c.x > parameter)
        else:
            stmt += lambda s: s.where(table.c.y == parameter)
        return stmt

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

  • Не используйте функции внутри лямбды для создания связанных значений - подход отслеживания связанных значений требует, чтобы фактическое значение, которое будет использоваться в SQL-операторе, локально присутствовало в закрытии лямбды. Это невозможно, если значения генерируются из других функций, и LambdaElement обычно должен вызывать ошибку при такой попытке:

    >>> def my_stmt(x, y):
    ...     def get_x():
    ...         return x
    ...
    ...     def get_y():
    ...         return y
    ...
    ...     stmt = lambda_stmt(lambda: select(func.max(get_x(), get_y())))
    ...     return stmt
    >>> with engine.connect() as conn:
    ...     print(conn.scalar(my_stmt(5, 10)))
    Traceback (most recent call last):
      # ...
    sqlalchemy.exc.InvalidRequestError: Can't invoke Python callable get_x()
    inside of lambda expression argument at
    <code object <lambda> at 0x7fed15f350e0, file "<stdin>", line 6>;
    lambda SQL constructs should not invoke functions from closure variables
    to produce literal values since the lambda SQL system normally extracts
    bound values without actually invoking the lambda or any functions within it.

    Выше, использование get_x() и get_y(), если они необходимы, должно происходить вне лямбды и присваиваться локальной закрывающей переменной:

    >>> def my_stmt(x, y):
    ...     def get_x():
    ...         return x
    ...
    ...     def get_y():
    ...         return y
    ...
    ...     x_param, y_param = get_x(), get_y()
    ...     stmt = lambda_stmt(lambda: select(func.max(x_param, y_param)))
    ...     return stmt
  • Избегайте ссылаться на не-SQL конструкции внутри лямбд, поскольку они не кэшируются по умолчанию - этот вопрос относится к тому, как LambdaElement создает ключ кэша из других переменных закрытия в операторе. Для того чтобы обеспечить наилучшую гарантию точного ключа кэша, все объекты, находящиеся в закрытии лямбды, считаются значимыми, и ни один из них не будет считаться подходящим для ключа кэша по умолчанию. Поэтому в следующем примере также будет выдано довольно подробное сообщение об ошибке:

    >>> class Foo:
    ...     def __init__(self, x, y):
    ...         self.x = x
    ...         self.y = y
    >>> def my_stmt(foo):
    ...     stmt = lambda_stmt(lambda: select(func.max(foo.x, foo.y)))
    ...     return stmt
    >>> with engine.connect() as conn:
    ...     print(conn.scalar(my_stmt(Foo(5, 10))))
    Traceback (most recent call last):
      # ...
    sqlalchemy.exc.InvalidRequestError: Closure variable named 'foo' inside of
    lambda callable <code object <lambda> at 0x7fed15f35450, file
    "<stdin>", line 2> does not refer to a cacheable SQL element, and also
    does not appear to be serving as a SQL literal bound value based on the
    default SQL expression returned by the function.  This variable needs to
    remain outside the scope of a SQL-generating lambda so that a proper cache
    key may be generated from the lambda's state.  Evaluate this variable
    outside of the lambda, set track_on=[<elements>] to explicitly select
    closure elements to track, or set track_closure_variables=False to exclude
    closure variables from being part of the cache key.

    Приведенная выше ошибка указывает на то, что LambdaElement не предполагает, что переданный объект Foo будет вести себя одинаково во всех случаях. Он также не предполагает, что может использовать Foo как часть ключа кэша по умолчанию; если бы он использовал объект Foo как часть ключа кэша, то при наличии множества различных объектов Foo он заполнил бы кэш дублирующейся информацией, а также хранил бы длительные ссылки на все эти объекты.

    Лучший способ разрешить описанную выше ситуацию - не ссылаться на foo внутри лямбды, а ссылаться на нее вне:

    >>> def my_stmt(foo):
    ...     x_param, y_param = foo.x, foo.y
    ...     stmt = lambda_stmt(lambda: select(func.max(x_param, y_param)))
    ...     return stmt

    В некоторых ситуациях, если гарантируется, что SQL-структура лямбды никогда не изменится на основе ввода, передать track_closure_variables=False, что отключит любое отслеживание закрывающих переменных, кроме тех, которые используются для связанных параметров:

    >>> def my_stmt(foo):
    ...     stmt = lambda_stmt(
    ...         lambda: select(func.max(foo.x, foo.y)), track_closure_variables=False
    ...     )
    ...     return stmt

    Существует также возможность добавить объекты в элемент, чтобы явно сформировать часть ключа кэша, используя параметр track_on; использование этого параметра позволяет определенным значениям служить в качестве ключа кэша, а также предотвращает рассмотрение других переменных закрытия. Это полезно для случаев, когда часть конструируемого SQL берет начало от какого-либо контекстного объекта, который может иметь множество различных значений. В приведенном ниже примере первый сегмент оператора SELECT отключит отслеживание переменной foo, в то время как второй сегмент будет явно отслеживать self как часть ключа кэша:

    >>> def my_stmt(self, foo):
    ...     stmt = lambda_stmt(
    ...         lambda: select(*self.column_expressions), track_closure_variables=False
    ...     )
    ...     stmt = stmt.add_criteria(lambda: self.where_criteria, track_on=[self])
    ...     return stmt

    Использование track_on означает, что заданные объекты будут долго храниться во внутреннем кэше лямбды и будут иметь сильные ссылки до тех пор, пока кэш не очистится от этих объектов (по умолчанию используется схема LRU из 1000 записей).

Генерация ключей кэша

Для понимания некоторых опций и поведения, которые возникают при использовании лямбда-конструкций SQL, полезно понимание системы кэширования.

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

>>> from sqlalchemy import select, column
>>> stmt = select(column("q"))
>>> cache_key = stmt._generate_cache_key()
>>> print(cache_key)  # somewhat paraphrased
CacheKey(key=(
  '0',
  <class 'sqlalchemy.sql.selectable.Select'>,
  '_raw_columns',
  (
    (
      '1',
      <class 'sqlalchemy.sql.elements.ColumnClause'>,
      'name',
      'q',
      'type',
      (
        <class 'sqlalchemy.sql.sqltypes.NullType'>,
      ),
    ),
  ),
  # a few more elements are here, and many more for a more
  # complicated SELECT statement
),)

Указанный выше ключ хранится в кэше, который по сути является словарем, а значение - это конструкция, которая, помимо всего прочего, хранит строковую форму SQL-запроса, в данном случае фразу «SELECT q». Мы можем заметить, что даже для очень короткого запроса ключ кэша довольно многословен, поскольку он должен представлять все, что может варьироваться о том, что отображается и потенциально выполняется.

Система построения лямбд, напротив, создает другой вид ключа кэша:

>>> from sqlalchemy import lambda_stmt
>>> stmt = lambda_stmt(lambda: select(column("q")))
>>> cache_key = stmt._generate_cache_key()
>>> print(cache_key)
CacheKey(key=(
  <code object <lambda> at 0x7fed1617c710, file "<stdin>", line 1>,
  <class 'sqlalchemy.sql.lambdas.StatementLambdaElement'>,
),)

Выше мы видим, что ключ кэша значительно короче, чем у утверждения без лямбды, и, кроме того, производство самой конструкции select(column("q")) даже не было необходимым; сама лямбда Python содержит атрибут __code__, который ссылается на объект кода Python, который во время выполнения приложения является неизменяемым и постоянным.

Когда лямбда также включает закрывающие переменные, в обычном случае, когда эти переменные ссылаются на конструкции SQL, такие как объекты столбцов, они становятся частью ключа кэша, или если они ссылаются на литеральные значения, которые будут связанными параметрами, они помещаются в отдельный элемент ключа кэша:

>>> def my_stmt(parameter):
...     col = column("q")
...     stmt = lambda_stmt(lambda: select(col))
...     stmt += lambda s: s.where(col == parameter)
...     return stmt

Приведенный выше StatementLambdaElement включает две ламбды, обе из которых ссылаются на закрывающую переменную col, поэтому ключ кэша будет представлять оба этих сегмента, а также объект column():

>>> stmt = my_stmt(5)
>>> key = stmt._generate_cache_key()
>>> print(key)
CacheKey(key=(
  <code object <lambda> at 0x7f07323c50e0, file "<stdin>", line 3>,
  (
    '0',
    <class 'sqlalchemy.sql.elements.ColumnClause'>,
    'name',
    'q',
    'type',
    (
      <class 'sqlalchemy.sql.sqltypes.NullType'>,
    ),
  ),
  <code object <lambda> at 0x7f07323c5190, file "<stdin>", line 4>,
  <class 'sqlalchemy.sql.lambdas.LinkedLambdaElement'>,
  (
    '0',
    <class 'sqlalchemy.sql.elements.ColumnClause'>,
    'name',
    'q',
    'type',
    (
      <class 'sqlalchemy.sql.sqltypes.NullType'>,
    ),
  ),
  (
    '0',
    <class 'sqlalchemy.sql.elements.ColumnClause'>,
    'name',
    'q',
    'type',
    (
      <class 'sqlalchemy.sql.sqltypes.NullType'>,
    ),
  ),
),)

Вторая часть кэш-ключа извлекает связанные параметры, которые будут использоваться при вызове оператора:

>>> key.bindparams
[BindParameter('%(139668884281280 parameter)s', 5, type_=Integer())]

Для серии примеров кэширования «лямбда» со сравнением производительности, смотрите набор тестов «short_selects» в примере производительности Производительность.

Поведение «Вставка многих значений» для операторов INSERT

Добавлено в версии 2.0: см. Оптимизированная массовая вставка ORM теперь реализована для всех бэкендов, кроме MySQL для получения информации об изменении, включая примеры тестов производительности

Совет

Функция insertmanyvalues - это прозрачно доступная функция производительности, которая не требует вмешательства конечного пользователя для того, чтобы она выполнялась по мере необходимости. В этом разделе описывается архитектура функции, а также способы измерения ее производительности и настройки ее поведения для оптимизации скорости выполнения массовых операторов INSERT, особенно при использовании ORM.

Поскольку все больше баз данных добавляют поддержку INSERT…RETURNING, SQLAlchemy претерпела значительные изменения в подходе к операциям INSERT, в которых необходимо получить значения, генерируемые сервером, а главное, значения первичного ключа, которые позволяют ссылаться на новый ряд в последующих операциях. В частности, этот сценарий долгое время был серьезной проблемой производительности в ORM, который полагается на возможность получения генерируемых сервером значений первичного ключа, чтобы правильно заполнить identity map.

С недавней поддержкой RETURNING, добавленной в SQLite и MariaDB, SQLAlchemy больше не нужно полагаться на атрибут cursor.lastrowid только для одного ряда, предоставляемый DBAPI для большинства бэкендов; RETURNING теперь может использоваться для всех бэкендов SQLAlchemy-included за исключением MySQL. Оставшееся ограничение производительности, заключающееся в том, что метод cursor.executemany() DBAPI не позволяет извлекать строки, решается для большинства бэкендов путем отказа от использования executemany() и вместо этого реструктуризации отдельных операторов INSERT для размещения большого количества строк в одном операторе, который вызывается с помощью cursor.execute(). Этот подход берет свое начало от функции psycopg2 fast execution helpers DBAPI psycopg2, которую SQLAlchemy постепенно добавляла все больше и больше поддержки в последних сериях релизов.

Текущая поддержка

Функция включена для всех бэкендов, включенных в SQLAlchemy, которые поддерживают RETURNING, за исключением Oracle, для которого драйверы cx_Oracle и OracleDB предлагают свои собственные эквивалентные функции. Функция обычно используется при использовании метода Insert.returning() конструкции Insert в сочетании с выполнением executemany, что происходит при передаче списка словарей в параметр Connection.execute.parameters методов Connection.execute() или Session.execute() (а также эквивалентных методов asyncio и сокращенных методов Session.scalars()). Это также происходит в рамках процесса ORM unit of work при использовании таких методов, как Session.add() и Session.add_all() для добавления строк.

Для включенных диалектов SQLAlchemy поддержка или эквивалентная поддержка в настоящее время выглядит следующим образом:

  • SQLite - поддерживается для SQLite версии 3.35 и выше

  • PostgreSQL - все поддерживаемые версии Postgresql (9 и выше)

  • SQL Server - все поддерживаемые версии SQL Server [1]

  • MariaDB - поддерживается для MariaDB версии 10.5 и выше

  • MySQL - нет поддержки, функция RETURNING отсутствует

  • Oracle - поддерживает ПОЛУЧЕНИЕ с executemany, используя родные API cx_Oracle / OracleDB, для всех поддерживаемых версий Oracle 9 и выше, используя многорядные параметры OUT. Это не та же реализация, что и «executemanyvalues», однако имеет те же схемы использования и эквивалентные преимущества в производительности.

Изменено в версии 2.0.10:

Отключение функции

Чтобы отключить функцию «insertmanyvalues» для данного бэкенда в целом Engine, передайте параметр create_engine.use_insertmanyvalues как False в create_engine():

engine = create_engine(
    "mariadb+mariadbconnector://scott:tiger@host/db", use_insertmanyvalues=False
)

Функцию также можно отключить от неявного использования для конкретного объекта Table, передав параметр Table.implicit_returning в виде False:

t = Table(
    "t",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("x", Integer),
    implicit_returning=False,
)

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

Работа в пакетном режиме

Эта функция имеет два режима работы, которые выбираются прозрачно на основе каждого диалекта, каждого:class:_schema.Table. Один из них - батированный режим, который сокращает количество обходов базы данных, переписывая оператор INSERT в форме:

INSERT INTO a (data, x, y) VALUES (%(data)s, %(x)s, %(y)s) RETURNING a.id

в «пакетную» форму, например:

INSERT INTO a (data, x, y) VALUES
    (%(data_0)s, %(x_0)s, %(y_0)s),
    (%(data_1)s, %(x_1)s, %(y_1)s),
    (%(data_2)s, %(x_2)s, %(y_2)s),
    ...
    (%(data_78)s, %(x_78)s, %(y_78)s)
RETURNING a.id

где выше, оператор организуется против подмножества («партии») входных данных, размер которых определяется бэкендом базы данных, а также количество параметров в каждой партии, чтобы соответствовать известным ограничениям на размер оператора / количество параметров. Затем функция выполняет оператор INSERT один раз для каждой партии входных данных, пока все записи не будут использованы, объединяя результаты RETURNING для каждой партии в один большой набор строк, который доступен из одного объекта Result.

Эта «пакетная» форма позволяет выполнять INSERT многих строк, используя гораздо меньше обходов базы данных, и, как было показано, позволяет значительно повысить производительность для большинства бэкендов, где она поддерживается.

Соотнесение строк RETURNING с наборами параметров

Добавлено в версии 2.0.10.

Запрос в «пакетном» режиме, проиллюстрированный в предыдущем разделе, не гарантирует, что порядок возвращаемых записей будет соответствовать порядку входных данных. При использовании процесса SQLAlchemy ORM unit of work, а также для приложений, которые соотносят возвращаемые серверные значения с входными данными, методы Insert.returning() и UpdateBase.return_defaults() включают опцию Insert.returning.sort_by_parameter_order, которая указывает, что режим «insertmanyvalues» должен гарантировать это соответствие. Это не связано с порядком, в котором записи фактически вставляются бэкендом базы данных, который не предполагается ни при каких обстоятельствах; только то, что возвращаемые записи должны быть организованы при получении обратно, чтобы соответствовать порядку, в котором были переданы исходные входные данные.

Когда присутствует параметр Insert.returning.sort_by_parameter_order, для таблиц, использующих генерируемые сервером целочисленные значения первичного ключа, такие как IDENTITY, PostgreSQL SERIAL, MariaDB AUTO_INCREMENT или схема SQLite ROWID, «пакетный» режим может вместо этого использовать более сложную форму INSERT…. RETURNING в сочетании с сортировкой строк после выполнения на основе возвращаемых значений, или, если такая форма недоступна, функция «insertmanyvalues» может плавно перейти в «непакетный» режим, который запускает отдельные операторы INSERT для каждого набора параметров.

Например, на SQL Server, когда в качестве первичного ключа используется автоинкрементирующий столбец IDENTITY, используется следующая SQL-форма:

INSERT INTO a (data, x, y)
OUTPUT inserted.id, inserted.id AS id__1
SELECT p0, p1, p2 FROM (VALUES
    (?, ?, ?, 0), (?, ?, ?, 1), (?, ?, ?, 2),
    ...
    (?, ?, ?, 77)
) AS imp_sen(p0, p1, p2, sen_counter) ORDER BY sen_counter

Аналогичная форма используется и в PostgreSQL, когда столбцы первичного ключа используют SERIAL или IDENTITY. Приведенная выше форма не гарантирует порядок вставки строк. Однако она гарантирует, что значения IDENTITY или SERIAL будут созданы по порядку с каждым набором параметров [2]. Функция «insertmanyvalues» сортирует возвращаемые строки для приведенного выше оператора INSERT по возрастанию целочисленной идентичности.

Для базы данных SQLite не существует соответствующей формы INSERT, которая могла бы соотнести получение новых значений ROWID с порядком передачи наборов параметров. В результате, при использовании генерируемых сервером значений первичного ключа, бэкенд SQLite переходит в «непакетный» режим, когда запрашивается упорядоченный RETURNING. Для MariaDB достаточно формы INSERT по умолчанию, используемой insertmanyvalues, поскольку при использовании InnoDB [3] бэкенд этой базы данных выстроит порядок AUTO_INCREMENT в соответствии с порядком входных данных.

Для первичного ключа, сгенерированного на стороне клиента, например, при использовании функции Python uuid.uuid4() для генерации новых значений для столбца Uuid, функция «insertmanyvalues» прозрачно включает этот столбец в записи ПОЛУЧЕНИЯ и соотносит его значение со значением данных входных записей, тем самым поддерживая соответствие между входными записями и строками результата. Из этого следует, что все бэкенды допускают пакетный, коррелированный с параметрами порядок ПОЛУЧЕНИЯ, когда используются генерируемые клиентом значения первичного ключа.

Тема о том, как «пакетный» режим «insertmanyvalues» определяет столбец или столбцы для использования в качестве точки соответствия между входными параметрами и строками RETURNING, известна как insert sentinel, которая представляет собой конкретный столбец или столбцы, используемые для отслеживания таких значений. Столбец «insert sentinel» обычно выбирается автоматически, однако может также настраиваться пользователем для особо особых случаев; это описано в разделе Настройка столбцов часовых.

Для бэкендов, не предлагающих соответствующую форму INSERT, которая может предоставлять генерируемые сервером значения, детерминированно согласованные с входными значениями, или для конфигураций Table, в которых используются другие виды генерируемых сервером значений первичного ключа, режим «insertmanyvalues» будет использовать режим non-batched, когда запрашивается гарантированный порядок RETURNING.

См.также

Работа в непакетном режиме

Для конфигураций Table, которые не имеют значений первичного ключа на стороне клиента и предлагают значения первичного ключа, генерируемые сервером (или не имеют первичного ключа), которые рассматриваемая база данных не способна вызывать детерминированным или сортируемым способом относительно множества наборов параметров, функция «insertmanyvalues» при выполнении требования Insert.returning.sort_by_parameter_order для оператора Insert может вместо этого использовать non-batched mode.

В этом режиме сохраняется оригинальная SQL-форма INSERT, а функция «insertmanyvalues» вместо этого будет выполнять оператор, как указано, для каждого набора параметров в отдельности, организуя возвращаемые строки в полный набор результатов. В отличие от предыдущих версий SQLAlchemy, она делает это в узком цикле, что минимизирует накладные расходы Python. В некоторых случаях, например, на SQLite, режим «non-batched» работает точно так же, как и режим «batched».

Модель исполнения заявлений

Как для «пакетного», так и для «не пакетного» режимов, функция обязательно вызовет многочисленные операторы INSERT, используя метод DBAPI cursor.execute(), в рамках одного вызова метода Core-level Connection.execute(), причем каждый оператор будет содержать до фиксированного предела наборов параметров. Это ограничение настраивается, как описано ниже в Управление размером партии. Отдельные вызовы cursor.execute() регистрируются по отдельности, а также по отдельности передаются слушателям событий, таким как ConnectionEvents.before_cursor_execute() (см. Ведение журнала и события ниже).

Настройка столбцов часовых

В типичных случаях функция «insertmanyvalues» для обеспечения INSERT…RETURNING с детерминированным порядком следования строк будет автоматически определять отправной столбец из первичного ключа данной таблицы, плавно переходя в режим «строка за строкой», если таковой не может быть определен. В качестве совершенно опциональной возможности, чтобы получить полную производительность массового метода «insertmanyvalues» для таблиц, имеющих генерируемые сервером первичные ключи, функции генератора которых по умолчанию несовместимы со случаем использования «sentinel», другие столбцы, не являющиеся первичными ключами, могут быть помечены как столбцы «sentinel», если они отвечают определенным требованиям. Типичным примером является столбец не основного ключа Uuid с функцией по умолчанию на стороне клиента, такой как функция Python uuid.uuid4(). Существует также конструкция для создания простых целочисленных столбцов с целочисленным счетчиком на стороне клиента, ориентированная на случай использования «insertmanyvalues».

Столбцы-отправители могут быть указаны путем добавления Column.insert_sentinel к квалифицирующим столбцам. Самый простой «квалифицирующий» столбец - это не нулевой, уникальный столбец, имеющий значение по умолчанию на стороне клиента, например, UUID столбец, как показано ниже:

import uuid

from sqlalchemy import Column
from sqlalchemy import FetchedValue
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import Uuid

my_table = Table(
    "some_table",
    metadata,
    # assume some arbitrary server-side function generates
    # primary key values, so cannot be tracked by a bulk insert
    Column("id", String(50), server_default=FetchedValue(), primary_key=True),
    Column("data", String(50)),
    Column(
        "uniqueid",
        Uuid(),
        default=uuid.uuid4,
        nullable=False,
        unique=True,
        insert_sentinel=True,
    ),
)

При использовании декларативных моделей ORM те же формы доступны с помощью конструкции mapped_column:

import uuid

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class MyClass(Base):
    __tablename__ = "my_table"

    id: Mapped[str] = mapped_column(primary_key=True, server_default=FetchedValue())
    data: Mapped[str] = mapped_column(String(50))
    uniqueid: Mapped[uuid.UUID] = mapped_column(
        default=uuid.uuid4, unique=True, insert_sentinel=True
    )

Хотя значения, генерируемые генератором по умолчанию, должны быть уникальными, фактическое ограничение UNIQUE на вышеупомянутый столбец «sentinel», указываемое параметром unique=True, само по себе является необязательным и может быть опущено, если это нежелательно.

Существует также специальная форма «insert sentinel», которая представляет собой специальный нулевой целочисленный столбец, использующий специальный целочисленный счетчик по умолчанию, который используется только во время операций «insertmanyvalues»; в качестве дополнительного поведения, столбец будет исключать себя из SQL-операторов и наборов результатов и вести себя в основном прозрачным образом. Тем не менее, он должен физически присутствовать в реальной таблице базы данных. Этот стиль Column может быть построен с помощью функции insert_sentinel():

from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import Uuid
from sqlalchemy import insert_sentinel

Table(
    "some_table",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("data", String(50)),
    insert_sentinel("sentinel"),
)

При использовании ORM Declarative доступна версия insert_sentinel(), дружественная к декларативности, под названием orm_insert_sentinel(), которая может быть использована для базового класса или миксина; если упакована с использованием declared_attr(), колонка будет применяться ко всем связанным с таблицей подклассам, в том числе внутри объединенных иерархий наследования:

from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import orm_insert_sentinel


class Base(DeclarativeBase):
    @declared_attr
    def _sentinel(cls) -> Mapped[int]:
        return orm_insert_sentinel()


class MyClass(Base):
    __tablename__ = "my_table"

    id: Mapped[str] = mapped_column(primary_key=True, server_default=FetchedValue())
    data: Mapped[str] = mapped_column(String(50))


class MySubClass(MyClass):
    __tablename__ = "sub_table"

    id: Mapped[str] = mapped_column(ForeignKey("my_table.id"), primary_key=True)


class MySingleInhClass(MyClass):
    pass

В приведенном выше примере и «my_table», и «sub_table» будут иметь дополнительный целочисленный столбец с именем «_sentinel», который может быть использован функцией «insertmanyvalues» для оптимизации массовых вставок, используемых ORM.

Управление размером партии

Ключевой характеристикой «insertmanyvalues» является то, что размер оператора INSERT ограничен фиксированным максимальным количеством клаузул «values», а также специфическим для диалекта фиксированным общим количеством связанных параметров, которые могут быть представлены в одном операторе INSERT за один раз. Когда количество заданных словарей параметров превышает фиксированный предел, или когда общее количество связанных параметров, которые должны быть представлены в одном операторе INSERT, превышает фиксированный предел (эти два фиксированных предела раздельны), в рамках одного вызова Connection.execute() будет вызвано несколько операторов INSERT, каждый из которых вмещает часть словарей параметров, называемых «пакет». Количество словарей параметров, представленных в каждой «партии», называется «размером партии». Например, размер пакета 500 означает, что каждый оператор INSERT будет выполнять INSERT не более 500 строк.

:class:`.Engine`Потенциально важно иметь возможность регулировать размер пакета, так как больший размер пакета может быть более производительным для INSERT, где сами наборы значений относительно малы, а меньший размер пакета может быть более подходящим для INSERT, который использует очень большие наборы значений, где как размер визуализированного SQL, так и общий размер данных, передаваемых в одном операторе, может быть выгодно ограничить до определенного размера на основе поведения бэкенда и ограничений памяти. По этой причине размер пакета может быть настроен как для каждого оператора, так и для каждой операции. С другой стороны, ограничение параметра фиксируется на основе известных характеристик используемой базы данных.

Размер пакета по умолчанию равен 1000 для большинства бэкендов, с дополнительным ограничивающим фактором «максимальное количество параметров» для каждого диалекта, который может уменьшить размер пакета на основе каждого запроса. Максимальное количество параметров зависит от диалекта и версии сервера; самый большой размер - 32700 (выбран как здоровое расстояние от ограничения PostgreSQL в 32767 и современного ограничения SQLite в 32766, оставляя при этом место для дополнительных параметров в операторе, а также для причуд DBAPI). В старых версиях SQLite (до 3.32.0) это значение равно 999. MariaDB не имеет установленного предела, однако 32700 остается ограничивающим фактором для размера SQL-сообщения.

На значение «размера партии» можно влиять Engine в широком диапазоне с помощью параметра create_engine.insertmanyvalues_page_size. Например, чтобы повлиять на операторы INSERT, чтобы включить до 100 наборов параметров в каждый оператор:

e = create_engine("sqlite://", insertmanyvalues_page_size=100)

Размер пакета также может быть изменен для каждого оператора с помощью опции выполнения Connection.execution_options.insertmanyvalues_page_size, например, per execution:

with e.begin() as conn:
    result = conn.execute(
        table.insert().returning(table.c.id),
        parameterlist,
        execution_options={"insertmanyvalues_page_size": 100},
    )

Или настроен на само заявление:

stmt = (
    table.insert()
    .returning(table.c.id)
    .execution_options(insertmanyvalues_page_size=100)
)
with e.begin() as conn:
    result = conn.execute(stmt, parameterlist)

Ведение журнала и события

Функция «insertmanyvalues» полностью интегрируется с statement logging SQLAlchemy, а также с событиями курсора, такими как ConnectionEvents.before_cursor_execute(). Когда список параметров разбивается на отдельные партии, каждый оператор INSERT регистрируется и передается обработчикам событий по отдельности. Это существенное изменение по сравнению с тем, как эта функция работала только для psycopg2 в предыдущих сериях SQLAlchemy 1.x, где создание нескольких операторов INSERT было скрыто от протоколирования и событий. Отображение журнала будет усекать длинные списки параметров для удобочитаемости, а также будет указывать на конкретную партию каждого оператора. Приведенный ниже пример иллюстрирует выдержку из этого протоколирования:

INSERT INTO a (data, x, y) VALUES (?, ?, ?), ... 795 characters truncated ...  (?, ?, ?), (?, ?, ?) RETURNING id
[generated in 0.00177s (insertmanyvalues) 1/10 (unordered)] ('d0', 0, 0, 'd1',  ...
INSERT INTO a (data, x, y) VALUES (?, ?, ?), ... 795 characters truncated ...  (?, ?, ?), (?, ?, ?) RETURNING id
[insertmanyvalues 2/10 (unordered)] ('d100', 100, 1000, 'd101', ...

...

INSERT INTO a (data, x, y) VALUES (?, ?, ?), ... 795 characters truncated ...  (?, ?, ?), (?, ?, ?) RETURNING id
[insertmanyvalues 10/10 (unordered)] ('d900', 900, 9000, 'd901', ...

Когда происходит non-batch mode, протоколирование покажет это вместе с сообщением insertmanyvalues:

...

INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 67/78 (ordered; batch not supported)] ('d66', 66, 66)
INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 68/78 (ordered; batch not supported)] ('d67', 67, 67)
INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 69/78 (ordered; batch not supported)] ('d68', 68, 68)
INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 70/78 (ordered; batch not supported)] ('d69', 69, 69)

...

Поддержка Upsert

Диалекты PostgreSQL, SQLite и MariaDB предлагают специфические для бэкенда конструкции «upsert» insert(), insert() и insert(), которые представляют собой конструкции Insert, имеющие дополнительный метод, такой как on_conflict_do_update()` or ``on_duplicate_key(). Эти конструкции также поддерживают поведение «insertmanyvalues», когда они используются с RETURNING, что позволяет выполнять эффективные вставки с RETURNING.

Утилизация двигателей

Engine ссылается на пул соединений, что означает, что при нормальных обстоятельствах существуют открытые соединения с базой данных, пока объект Engine все еще находится в памяти. Когда Engine собирается в мусор, его пул соединений больше не упоминается этим Engine, и если ни одно из его соединений все еще не проверено, пул и его соединения также будут собраны в мусор, что приведет к закрытию фактических соединений с базой данных. Но в противном случае Engine будет удерживать открытые соединения с базой данных, предполагая, что он использует обычно стандартную реализацию пула QueuePool.

Реестр Engine предназначен для того, чтобы быть постоянным элементом, созданным заранее и поддерживаемым в течение всего срока службы приложения. Он **не предназначен для создания и утилизации каждого соединения; это реестр, который поддерживает пул соединений, а также конфигурационную информацию об используемой базе данных и DBAPI, а также некоторую степень внутреннего кэширования ресурсов каждой базы данных.

Однако существует множество случаев, когда желательно, чтобы все ресурсы соединения, на которые ссылается Engine, были полностью закрыты. Обычно в таких случаях не стоит полагаться на сборку мусора в Python; вместо этого Engine можно явно утилизировать с помощью метода Engine.dispose(). При этом утилизируется базовый пул соединений движка и заменяется новым, пустым. Если в этот момент Engine будет удален и больше не будет использоваться, то все зарегистрированные соединения, на которые он ссылается, также будут полностью закрыты.

Важные случаи использования вызова Engine.dispose() включают:

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

  • Когда программа использует многопроцессорность или fork(), и объект Engine копируется в дочерний процесс, следует вызвать Engine.dispose(), чтобы движок создал совершенно новые соединения базы данных, локальные для этого форка. Соединения баз данных обычно не пересекают границы процессов. В этом случае используйте параметр Engine.dispose.close, установленный в False. Подробнее об этом случае см. в разделе Использование пулов соединений с многопроцессорной обработкой или os.fork().

  • В тестовых наборах или в сценариях с многопользовательской лицензией, где может создаваться и утилизироваться множество специальных, недолговечных объектов Engine.

Соединения, которые отмечены, не отбрасываются при утилизации или сборке мусора, поскольку эти соединения все еще активно используются приложением. Однако после вызова Engine.dispose() эти соединения больше не ассоциируются с этим Engine; когда они закрываются, они возвращаются в свой теперь уже орфанный пул соединений, который в конечном итоге будет собран в мусор, когда все соединения, ссылающиеся на него, также перестанут на него ссылаться. Поскольку этот процесс нелегко контролировать, настоятельно рекомендуется вызывать Engine.dispose() только после того, как все проверенные соединения будут зарегистрированы или иным образом деассоциированы из их пула.

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

Работа с драйверами SQL и необработанными соединениями DBAPI

Во введении об использовании Connection.execute() использовалась конструкция text(), чтобы проиллюстрировать, как можно вызывать текстовые операторы SQL. При работе с SQLAlchemy текстовый SQL является скорее исключением, чем нормой, поскольку язык выражений Core и ORM абстрагируются от текстового представления SQL. Однако сама конструкция text() также обеспечивает некоторую абстракцию текстового SQL, поскольку она нормализует передачу связанных параметров, а также поддерживает поведение типизации данных для параметров и строк набора результатов.

Вызов строк SQL непосредственно драйвером

Для случая, когда необходимо вызвать текстовый SQL, непосредственно переданный базовому драйверу (известный как DBAPI) без вмешательства конструкции text(), можно использовать метод Connection.exec_driver_sql():

with engine.connect() as conn:
    conn.exec_driver_sql("SET param='bar'")

Добавлено в версии 1.4: Добавлен метод Connection.exec_driver_sql().

Работа с курсором DBAPI напрямую

В некоторых случаях SQLAlchemy не предоставляет обобщенного способа доступа к некоторым функциям DBAPI, таким как вызов хранимых процедур, а также работа с несколькими наборами результатов. В этих случаях целесообразно работать с необработанным DBAPI-соединением напрямую.

Наиболее распространенный способ доступа к необработанному соединению DBAPI - получить его из уже присутствующего объекта Connection напрямую. Он присутствует с помощью атрибута Connection.connection:

connection = engine.connect()
dbapi_conn = connection.connection

DBAPI-соединение здесь фактически является «проксированным» с точки зрения пула исходных соединений, однако это деталь реализации, которую в большинстве случаев можно игнорировать. Поскольку это DBAPI-соединение все еще находится в области видимости объекта-владельца Connection, лучше всего использовать объект Connection для большинства функций, таких как управление транзакциями, а также вызов метода Connection.close(); если эти операции выполняются непосредственно на DBAPI-соединении, объект-владелец Connection не будет знать об этих изменениях в состоянии.

Чтобы преодолеть ограничения, накладываемые соединением DBAPI, которое поддерживается владельцем Connection, соединение DBAPI также доступно без необходимости сначала приобретать Connection, используя метод Engine.raw_connection() из Engine:

dbapi_conn = engine.raw_connection()

Это соединение DBAPI, как и в предыдущем случае, является «проксированной» формой. Цель этого проксирования теперь очевидна, поскольку когда мы вызываем метод .close() этого соединения, соединение DBAPI обычно не закрывается, а вместо этого released возвращается в пул соединений движка:

dbapi_conn.close()

Хотя SQLAlchemy может в будущем добавить встроенные паттерны для большего количества случаев использования DBAPI, отдача от них снижается, так как эти случаи, как правило, нужны редко, и они также сильно зависят от типа используемого DBAPI, поэтому в любом случае паттерн прямого вызова DBAPI всегда доступен для тех случаев, когда он необходим.

См.также

Как получить необработанное соединение DBAPI при использовании Engine? - включает дополнительные подробности о том, как осуществляется доступ к соединению DBAPI, а также к соединению «драйвера» при использовании драйверов asyncio.

Ниже приведены некоторые рецепты использования соединений DBAPI.

Вызов хранимых процедур и функций, определяемых пользователем

SQLAlchemy поддерживает вызов хранимых процедур и функций, определенных пользователем, несколькими способами. Пожалуйста, обратите внимание, что все DBAPI имеют различные практики, поэтому вы должны обратиться к документации вашей базовой DBAPI для уточнения специфики вашего конкретного использования. Следующие примеры являются гипотетическими и могут не работать с вашим базовым DBAPI.

Для хранимых процедур или функций с особыми синтаксическими особенностями или параметрами потенциально может быть использован уровень DBAPI callproc с вашим DBAPI. Примером такого шаблона является:

connection = engine.raw_connection()
try:
    cursor_obj = connection.cursor()
    cursor_obj.callproc("my_procedure", ["x", "y", "z"])
    results = list(cursor_obj.fetchall())
    cursor_obj.close()
    connection.commit()
finally:
    connection.close()

Примечание

Не все DBAPI используют callproc, и общие детали использования будут отличаться. Приведенный выше пример является лишь иллюстрацией того, как может выглядеть использование той или иной функции DBAPI.

Ваш DBAPI может не иметь требования callproc или может требовать, чтобы хранимая процедура или функция, определяемая пользователем, вызывалась с другим шаблоном, например, с обычным использованием соединения SQLAlchemy. Одним из примеров такого использования является, на момент написания этой документации, выполнение хранимой процедуры в базе данных PostgreSQL с помощью psycopg2 DBAPI, которая должна вызываться с обычным использованием соединения:

connection.execute("CALL my_procedure();")

Приведенный выше пример является гипотетическим. Не гарантируется, что базовая база данных поддерживает «CALL» или «SELECT» в этих ситуациях, и ключевое слово может меняться в зависимости от того, является ли функция хранимой процедурой или функцией, определенной пользователем. В таких ситуациях следует обратиться к документации по DBAPI и базе данных, чтобы определить правильный синтаксис и шаблоны для использования.

Множественные наборы результатов

Поддержка нескольких наборов результатов доступна из необработанного курсора DBAPI с помощью метода nextset:

connection = engine.raw_connection()
try:
    cursor_obj = connection.cursor()
    cursor_obj.execute("select * from table1; select * from table2")
    results_one = cursor_obj.fetchall()
    cursor_obj.nextset()
    results_two = cursor_obj.fetchall()
    cursor_obj.close()
finally:
    connection.close()

Регистрация новых диалектов

Вызов функции create_engine() определяет местонахождение заданного диалекта с помощью точек входа setuptools. Эти точки входа могут быть установлены для диалектов сторонних разработчиков в сценарии setup.py. Например, чтобы создать новый диалект «foodialect://», необходимо выполнить следующие действия:

  1. Создайте пакет под названием foodialect.

  2. В пакете должен быть модуль, содержащий класс диалекта, который обычно является подклассом sqlalchemy.engine.default.DefaultDialect. В этом примере допустим, что он называется FooDialect и доступ к его модулю осуществляется через foodialect.dialect.

  3. Точка входа может быть установлена в setup.cfg следующим образом:

    [options.entry_points]
    sqlalchemy.dialects =
        foodialect = foodialect.dialect:FooDialect

Если диалект обеспечивает поддержку определенного DBAPI поверх существующей базы данных, поддерживаемой SQLAlchemy, имя может быть дано с учетом квалификации базы данных. Например, если бы FooDialect был диалектом MySQL, то точка входа могла бы быть установлена следующим образом:

[options.entry_points]
sqlalchemy.dialects
    mysql.foodialect = foodialect.dialect:FooDialect

Тогда доступ к вышеуказанной точке входа будет осуществляться как create_engine("mysql+foodialect://").

Регистрация диалектов в процессе

SQLAlchemy также позволяет регистрировать диалект в текущем процессе, минуя необходимость отдельной установки. Используйте функцию register() следующим образом:

from sqlalchemy.dialects import registry


registry.register("mysql.foodialect", "myapp.dialect", "MyMySQLDialect")

Вышеприведенное ответит на create_engine("mysql+foodialect://") и загрузит класс MyMySQLDialect из модуля myapp.dialect.

Подключение / API двигателя

Object Name Description

Connection

Обеспечивает высокоуровневую функциональность для обернутого соединения DB-API.

CreateEnginePlugin

Набор крючков, предназначенных для дополнения построения объекта Engine на основе имен точек входа в URL.

Engine

Соединяет Pool и Dialect вместе, чтобы обеспечить источник подключения к базе данных и поведение.

ExceptionContext

Инкапсулировать информацию о текущем состоянии ошибки.

NestedTransaction

Представляют собой «вложенную», или SAVEPOINT транзакцию.

RootTransaction

Представьте «корневую» транзакцию на Connection.

Transaction

Представляет собой транзакцию базы данных, находящуюся в процессе выполнения.

TwoPhaseTransaction

Представляют собой двухфазную транзакцию.

class sqlalchemy.engine.Connection

Обеспечивает высокоуровневую функциональность для обернутого соединения DB-API.

Объект Connection приобретается путем вызова метода Engine.connect() объекта Engine и предоставляет услуги для выполнения операторов SQL, а также управления транзакциями.

Объект Connection не является потокобезопасным. Хотя Connection может быть совместно использован потоками с помощью правильно синхронизированного доступа, все же возможно, что базовое соединение DBAPI может не поддерживать совместный доступ между потоками. Для получения подробной информации обратитесь к документации DBAPI.

Объект Connection представляет собой одно соединение DBAPI, проверенное из пула соединений. В этом состоянии пул соединений не имеет никакого влияния на соединение, включая его истечение срока действия или состояние таймаута. Чтобы пул соединений правильно управлял соединениями, соединения должны быть возвращены в пул соединений (т.е. connection.close()) всякий раз, когда соединение не используется.

Классная подпись

класс sqlalchemy.engine.Connection (sqlalchemy.engine.interfaces.ConnectionEventsTarget, sqlalchemy.inspection.Inspectable)

method sqlalchemy.engine.Connection.__init__(engine: Engine, connection: Optional[PoolProxiedConnection] = None, _has_events: Optional[bool] = None, _allow_revalidate: bool = True, _allow_autobegin: bool = True)

Создайте новое Соединение.

method sqlalchemy.engine.Connection.begin() RootTransaction

Начать транзакцию до того, как произойдет автозапуск.

Например:

with engine.connect() as conn:
    with conn.begin() as trans:
        conn.execute(table.insert(), {"username": "sandy"})

Возвращаемый объект является экземпляром RootTransaction. Этот объект представляет собой «область действия» транзакции, которая завершается при вызове метода Transaction.rollback() или Transaction.commit(); объект также работает как менеджер контекста, как показано выше.

Метод Connection.begin() начинает транзакцию, которая обычно начинается в любом случае, когда соединение впервые используется для выполнения оператора. Этот метод может быть использован для вызова события ConnectionEvents.begin() в определенное время или для организации кода в рамках проверки соединения в терминах управляемых контекстом блоков, таких как:

with engine.connect() as conn:
    with conn.begin():
        conn.execute(...)
        conn.execute(...)

    with conn.begin():
        conn.execute(...)
        conn.execute(...)

Приведенный выше код принципиально не отличается по своему поведению от следующего кода, в котором не используется Connection.begin(); приведенный ниже стиль называется стилем «фиксация по мере выполнения»:

with engine.connect() as conn:
    conn.execute(...)
    conn.execute(...)
    conn.commit()

    conn.execute(...)
    conn.execute(...)
    conn.commit()

С точки зрения базы данных, метод Connection.begin() не выдает никакого SQL и никак не изменяет состояние основного соединения DBAPI; в Python DBAPI нет понятия явного начала транзакции.

См.также

Работа с транзакциями и DBAPI - в Унифицированный учебник по SQLAlchemy

Connection.begin_nested() - использовать SAVEPOINT

Connection.begin_twophase() - использование двухфазной транзакции /XID

Engine.begin() - менеджер контекста, доступный из Engine

method sqlalchemy.engine.Connection.begin_nested() NestedTransaction

Начать вложенную транзакцию (т.е. SAVEPOINT) и вернуть дескриптор транзакции, который контролирует область действия SAVEPOINT.

Например:

with engine.begin() as connection:
    with connection.begin_nested():
        connection.execute(table.insert(), {"username": "sandy"})

Возвращаемый объект является экземпляром NestedTransaction, который включает транзакционные методы NestedTransaction.commit() и NestedTransaction.rollback(); для вложенной транзакции эти методы соответствуют операциям «RELEASE SAVEPOINT <name>» и «ROLLBACK TO SAVEPOINT <name>». Имя точки сохранения является локальным для объекта NestedTransaction и генерируется автоматически. Как и любой другой Transaction, NestedTransaction может использоваться в качестве менеджера контекста, как показано выше, который будет «освобождать» или «откатывать» в зависимости от того, была ли операция внутри блока успешной или вызвала исключение.

Вложенные транзакции требуют поддержки SAVEPOINT в базовой базе данных, в противном случае поведение не определено. SAVEPOINT обычно используется для выполнения операций внутри транзакции, которые могут завершиться неудачей, при этом внешняя транзакция продолжается. Например:

from sqlalchemy import exc

with engine.begin() as connection:
    trans = connection.begin_nested()
    try:
        connection.execute(table.insert(), {"username": "sandy"})
        trans.commit()
    except exc.IntegrityError:  # catch for duplicate username
        trans.rollback()  # rollback to savepoint

    # outer transaction continues
    connection.execute( ... )

Если Connection.begin_nested() вызывается без предварительного вызова Connection.begin() или Engine.begin(), то объект Connection будет «автозапускать» сначала внешнюю транзакцию. Эта внешняя транзакция может быть зафиксирована в стиле «commit-as-you-go», например:

with engine.connect() as connection:  # begin() wasn't called

    with connection.begin_nested(): will auto-"begin()" first
        connection.execute( ... )
    # savepoint is released

    connection.execute( ... )

    # explicitly commit outer transaction
    connection.commit()

    # can continue working with connection here

Изменено в версии 2.0: Connection.begin_nested() теперь будет участвовать в поведении соединения «autobegin», которое является новым в версии 2.0 / соединения в стиле «будущего» в версии 1.4.

См.также

Connection.begin()

Использование SAVEPOINT - Поддержка ORM для SAVEPOINT

method sqlalchemy.engine.Connection.begin_twophase(xid: Optional[Any] = None) TwoPhaseTransaction

Начать двухфазную или XA-транзакцию и вернуть дескриптор транзакции.

Возвращаемый объект является экземпляром TwoPhaseTransaction, который в дополнение к методам, предоставляемым Transaction, также предоставляет метод TwoPhaseTransaction.prepare().

Параметры:

xid – идентификатор двухфазной транзакции. Если не указан, будет сгенерирован случайный идентификатор.

method sqlalchemy.engine.Connection.close() None

Закройте это Connection.

Это приводит к освобождению базовых ресурсов базы данных, то есть соединения DBAPI, на которое ссылается внутреннее соединение. DBAPI-соединение обычно восстанавливается обратно в соединение-держатель Pool, на которое ссылается Engine, породившее это Connection. Любое транзакционное состояние, присутствующее на соединении DBAPI, также безусловно освобождается через метод rollback() соединения DBAPI, независимо от любого объекта Transaction, который может быть непогашен в отношении этого Connection.

Это имеет эффект вызова Connection.rollback(), если выполняется какая-либо транзакция.

После вызова Connection.close() Connection навсегда переходит в закрытое состояние и не допускает дальнейших операций.

attribute sqlalchemy.engine.Connection.closed

Возвращает True, если это соединение закрыто.

method sqlalchemy.engine.Connection.commit() None

Зафиксировать транзакцию, которая в данный момент находится в процессе выполнения.

Этот метод фиксирует текущую транзакцию, если она была начата. Если транзакция не была начата, метод не имеет никакого эффекта, предполагая, что соединение находится в невалидированном состоянии.

Транзакция начинается на Connection автоматически при первом выполнении оператора или при вызове метода Connection.begin().

Примечание

Метод Connection.commit() действует только на первичную транзакцию базы данных, которая связана с объектом Connection. Он не действует на SAVEPOINT, который был бы вызван из метода Connection.begin_nested(); для управления SAVEPOINT вызовите NestedTransaction.commit() на NestedTransaction, который возвращается самим методом Connection.begin_nested().

attribute sqlalchemy.engine.Connection.connection

Базовое соединение DB-API, управляемое этим Соединением.

Это проксированное соединение пула соединений SQLAlchemy, которое затем имеет атрибут _ConnectionFairy.dbapi_connection, ссылающийся на реальное соединение драйвера.

attribute sqlalchemy.engine.Connection.default_isolation_level

Уровень изоляции времени начального подключения, связанный с используемым Dialect.

Это значение не зависит от опций выполнения Connection.execution_options.isolation_level и Engine.execution_options.isolation_level и определяется Dialect при создании первого соединения путем выполнения SQL-запроса к базе данных для текущего уровня изоляции до того, как будут выданы какие-либо дополнительные команды.

Вызов этого аксессора не вызывает никаких новых SQL-запросов.

См.также

Connection.get_isolation_level() - просмотр текущего фактического уровня изоляции

create_engine.isolation_level - устанавливается на уровень изоляции Engine

Connection.execution_options.isolation_level - устанавливается на уровень изоляции Connection

method sqlalchemy.engine.Connection.detach() None

Отсоедините базовое соединение DB-API от пула соединений.

Например:

with engine.connect() as conn:
    conn.detach()
    conn.execute(text("SET search_path TO schema1, schema2"))

    # work with connection

# connection is fully closed (since we used "with:", can
# also call .close())

Этот экземпляр Connection останется пригодным для использования. При закрытии (или выходе из контекста менеджера контекста, как указано выше) соединение DB-API будет буквально закрыто, а не возвращено в исходный пул.

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

method sqlalchemy.engine.Connection.exec_driver_sql(statement: str, parameters: Optional[_DBAPIAnyExecuteParams] = None, execution_options: Optional[CoreExecuteOptionsParameter] = None) CursorResult[Any]

Выполняет строковый SQL-запрос на курсоре DBAPI напрямую, без каких-либо шагов компиляции SQL.

Это можно использовать для передачи любой строки непосредственно в метод cursor.execute() используемого DBAPI.

Параметры:
  • statement – Строка оператора, которая должна быть выполнена. Связанные параметры должны использовать paramstyle базового DBAPI, например, «qmark», «pyformat», «format» и т.д.

  • parameters – представляют связанные значения параметров, которые будут использоваться при выполнении. Формат один из следующих: словарь именованных параметров, кортеж позиционных параметров или список, содержащий либо словари, либо кортежи для поддержки множественного выполнения.

Результат:

a CursorResult. Например, несколько словарей: conn.exec_driver_sql( «INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)», [{«id»:1, «value»: «v1»}, {«id»:2, «value»: «v2»}]] ) Одиночный словарь:: conn.exec_driver_sql( «INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)», dict(id=1, value=»v1») ) Одиночный кортеж:: conn.exec_driver_sql( «INSERT INTO table (id, value) VALUES (?, ?)», (1, „v1“) ) … примечание:: Метод Connection.exec_driver_sql() не участвует в событиях ConnectionEvents.before_execute() и ConnectionEvents.after_execute(). Чтобы перехватить вызовы Connection.exec_driver_sql(), используйте ConnectionEvents.before_cursor_execute() и ConnectionEvents.after_cursor_execute(). … см. также:: PEP 249

method sqlalchemy.engine.Connection.execute(statement: Executable, parameters: Optional[_CoreAnyExecuteParams] = None, *, execution_options: Optional[CoreExecuteOptionsParameter] = None) CursorResult[Any]

Выполняет конструкцию оператора SQL и возвращает CursorResult.

Параметры:
  • statement – Оператор, который должен быть выполнен. Это всегда объект, находящийся в иерархии ClauseElement и Executable, включая: * Select * Insert, Update, Delete * TextClause и TextualSelect * DDL и объекты, которые наследуются от ExecutableDDLElement.

  • parameters – параметры, которые будут связаны в операторе. Это может быть либо словарь имен и значений параметров, либо изменяемая последовательность (например, список) словарей. Если передается список словарей, то при выполнении оператора будет использован метод DBAPI cursor.executemany(). Если передается один словарь, будет использоваться метод DBAPI cursor.execute().

  • execution_options – необязательный словарь опций выполнения, которые будут связаны с выполнением оператора. Этот словарь может содержать подмножество опций, принимаемых Connection.execution_options().

Результат:

объект Result.

method sqlalchemy.engine.Connection.execution_options(**opt: Any) Connection

Установка не SQL-опций для соединения, которые вступают в силу во время выполнения.

Этот метод изменяет данный Connection на месте; возвращаемым значением является тот же объект Connection, на котором был вызван метод. Обратите внимание, что это противоположно поведению методов execution_options на других объектах, таких как Engine.execution_options() и Executable.execution_options(). Это объясняется тем, что многие подобные опции выполнения обязательно изменяют состояние базового соединения DBAPI в любом случае, поэтому нет возможности сохранить эффект такой опции локализованным для «под» соединения.

Изменено в версии 2.0: Метод Connection.execution_options(), в отличие от других объектов с этим методом, модифицирует соединение на месте, не создавая его копию.

Как уже говорилось, метод Connection.execution_options() принимает любые произвольные параметры, включая имена, заданные пользователем. Все заданные параметры можно использовать различными способами, в том числе с помощью метода Connection.get_execution_options(). См. примеры в Executable.execution_options() и Engine.execution_options().

Ключевые слова, которые в настоящее время распознаются самим SQLAlchemy, включают все те, которые перечислены в разделе Executable.execution_options(), а также другие, специфичные для Connection.

Параметры:
  • compiled_cache – Доступно на: Connection, Engine. Словарь, в котором объекты Compiled будут кэшироваться, когда Connection компилирует выражение клаузы в объект Compiled. Этот словарь будет заменять кэш операторов, который может быть настроен на самом Engine. Если установлено значение None, кэширование отключено, даже если движок имеет настроенный размер кэша. Обратите внимание, что ORM использует свой собственный «скомпилированный» кэш для некоторых операций, включая операции flush. Кэширование, используемое ORM внутри, заменяет указанный здесь словарь кэша.

  • logging_token – Доступно на: Connection, Engine, Executable. Добавляет указанный строковый маркер, окруженный скобками, в сообщения журнала, регистрируемые соединением, т.е. в журнал, включенный либо с помощью флага create_engine.echo, либо с помощью регистратора logging.getLogger("sqlalchemy.engine"). Это позволяет иметь токен для каждого соединения или каждого суб-двигателя, что полезно для отладки сценариев одновременного соединения. … версия добавлена:: 1.4.0b2 .. seealso:: Настройка токенов для каждого соединения / суб-инженера - пример использования create_engine.logging_name - добавляет имя к имени, используемому самим объектом Python logger.

  • isolation_level – Доступно на: Connection, Engine. Устанавливает уровень изоляции транзакции для времени жизни этого объекта Connection. Допустимые значения включают те строковые значения, которые принимаются параметром create_engine.isolation_level, переданным в create_engine(). Эти уровни зависят от конкретной базы данных; смотрите документацию по отдельным диалектам для определения допустимых уровней. Опция isolation level применяет уровень изоляции, испуская утверждения на соединении DBAPI, и необходимо влияет на исходный объект Connection в целом. Уровень изоляции будет оставаться на заданном уровне до тех пор, пока не будет явно изменен, или когда само соединение DBAPI будет released в пул соединений, т.е. будет вызван метод Connection.close(), в это время обработчик событий будет испускать дополнительные заявления на соединение DBAPI, чтобы отменить изменение уровня изоляции. … примечание:: Опция выполнения isolation_level может быть установлена только перед вызовом метода Connection.begin(), а также перед выполнением любых SQL-запросов, которые в противном случае вызвали бы «автозапуск», или непосредственно после вызова Connection.commit() или Connection.rollback(). База данных не может изменить уровень изоляции для выполняющейся транзакции. … примечание:: Опция выполнения isolation_level неявно сбрасывается, если Connection аннулируется, например, с помощью метода Connection.invalidate(), или если происходит ошибка разъединения. Новое соединение, созданное после аннулирования, не будет иметь выбранный уровень изоляции, который будет применен к нему автоматически. … см. также:: Установка уровней изоляции транзакций, включая DBAPI Autocommit Connection.get_isolation_level() - просмотр текущего акк

  • no_parameters – Доступно на: Connection, Executable. При True, если конечный список параметров или словарь абсолютно пуст, будет вызывать оператор на курсоре как cursor.execute(statement), не передавая коллекцию параметров вообще. Некоторые DBAPI, такие как psycopg2 и mysql-python, считают знаки процентов значимыми только при наличии параметров; эта опция позволяет генерировать SQL, содержащий знаки процентов (и, возможно, другие символы), нейтральный по отношению к тому, выполняется ли он DBAPI или передается в сценарий, который впоследствии вызывается средствами командной строки.

  • stream_results – Доступно на: Connection, Executable. Указывает диалекту, что результаты должны быть «потоковыми», а не предварительно буферизованными, если это возможно. Для таких бэкендов, как PostgreSQL, MySQL и MariaDB, это указывает на использование «курсора на стороне сервера» в отличие от курсора на стороне клиента. Другие бэкенды, такие как Oracle, могут уже использовать курсоры на стороне сервера по умолчанию. Использование Connection.execution_options.stream_results обычно сочетается с установкой фиксированного количества строк, которые будут извлекаться партиями, чтобы обеспечить эффективную итерацию строк базы данных и в то же время не загружать все строки результатов в память сразу; это можно настроить на объекте Result с помощью метода Result.yield_per(), после того как выполнение вернет новый Result. Если Result.yield_per() не используется, режим работы Connection.execution_options.stream_results вместо этого будет использовать буфер динамического размера, который буферизирует наборы строк за один раз, увеличиваясь в каждой партии на основе фиксированного размера роста до предела, который может быть настроен с помощью параметра Connection.execution_options.max_row_buffer. При использовании ORM для выборки объектов ORM из результата, Result.yield_per() всегда должен использоваться с Connection.execution_options.stream_results, чтобы ORM не извлекал все строки в новые объекты ORM одновременно. Для типичного использования следует предпочесть вариант выполнения Connection.execution_options.yield_per, который устанавливает одновременно Connection.execution_options.stream_results и Result.yield_per(). Этот вариант поддерживается как на уровне ядра с помощью Connection, так и с помощью ORM Session; последний описан в Получение больших наборов результатов с доходностью за. … см. также:: Использование курсоров на стороне сервера (они же потоковые результаты) - справочная информация о Connection.execution_options.stream_results Connection.execution_options.max_row_buffer Connection.execution_options.yield_per Получение больших наборов результатов с доходностью за - в Руководство по составлению запросов в ORM, описывающем yield_per O

  • max_row_buffer – Доступно на: Connection, Executable. Устанавливает максимальный размер буфера, используемого при использовании опции выполнения Connection.execution_options.stream_results на бэкенде, поддерживающем курсоры на стороне сервера. Значение по умолчанию, если не указано, равно 1000. … см. также:: Connection.execution_options.stream_results Использование курсоров на стороне сервера (они же потоковые результаты)

  • yield_per – Доступно на: Connection, Executable. Применяется целочисленное значение, которое установит опцию выполнения Connection.execution_options.stream_results и вызовет Result.yield_per() сразу автоматически. Позволяет получить функциональность, эквивалентную той, которая присутствует при использовании этого параметра с ORM. … версия добавлена:: 1.4.40 .. seealso:: Использование курсоров на стороне сервера (они же потоковые результаты) - справочная информация и примеры по использованию курсоров на стороне сервера в Core. Получение больших наборов результатов с доходностью за - в Руководство по составлению запросов в ORM, описывающем версию ORM yield_per.

  • insertmanyvalues_page_size – Доступно на: Connection, Engine. Количество строк для форматирования в оператор INSERT, когда оператор использует режим «insertmanyvalues», который является страничной формой массовой вставки, используемой для многих бэкендов при использовании executemany выполнения обычно в сочетании с RETURNING. Значение по умолчанию равно 1000. Также может быть изменено для каждого двигателя с помощью параметра create_engine.insertmanyvalues_page_size. … версия добавлена:: 2.0 … seealso:: Поведение «Вставка многих значений» для операторов INSERT

  • schema_translate_map – Доступно на: Connection, Engine, Executable. Словарь, отображающий имена схем на имена схем, который будет применяться к элементу Table.schema каждого Table, встречающегося при компиляции элементов выражений SQL или DDL в строки; результирующее имя схемы будет преобразовано на основе присутствия в карте исходного имени. … seealso:: Перевод имен схем

См.также

Engine.execution_options()

Executable.execution_options()

Connection.get_execution_options()

Варианты выполнения ORM - документация по всем специфическим для ORM опциям исполнения

method sqlalchemy.engine.Connection.get_execution_options() _ExecuteOptions

Получить параметры, не относящиеся к SQL, которые будут действовать во время выполнения.

Добавлено в версии 1.3.

method sqlalchemy.engine.Connection.get_isolation_level() Literal['SERIALIZABLE', 'REPEATABLE READ', 'READ COMMITTED', 'READ UNCOMMITTED', 'AUTOCOMMIT']

Возвращает текущий фактический уровень изоляции, который присутствует в базе данных в пределах данного соединения.

Этот атрибут выполняет операцию SQL с базой данных для получения текущего уровня изоляции, поэтому возвращаемое значение является фактическим уровнем на базовом соединении DBAPI, независимо от того, как было установлено это состояние. Это будет один из четырех фактических режимов изоляции READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE. Он не будет включать настройку уровня изоляции AUTOCOMMIT. Диалекты сторонних разработчиков могут иметь дополнительные настройки уровня изоляции.

Примечание

Этот метод не сообщает об уровне изоляции AUTOCOMMIT, который является отдельной настройкой dbapi, не зависящей от фактического уровня изоляции. Когда используется AUTOCOMMIT, соединение с базой данных все еще имеет «традиционный» режим изоляции, который обычно является одним из четырех значений READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE.

Сравните с аксессором Connection.default_isolation_level, который возвращает уровень изоляции, присутствующий в базе данных при первоначальном подключении.

См.также

Connection.default_isolation_level - просмотр уровня по умолчанию

create_engine.isolation_level - устанавливается на уровень изоляции Engine

Connection.execution_options.isolation_level - устанавливается на уровень изоляции Connection

method sqlalchemy.engine.Connection.get_nested_transaction() Optional[NestedTransaction]

Возвращает текущую выполняющуюся вложенную транзакцию, если таковая имеется.

Добавлено в версии 1.4.

method sqlalchemy.engine.Connection.get_transaction() Optional[RootTransaction]

Возвращает текущую выполняющуюся корневую транзакцию, если таковая имеется.

Добавлено в версии 1.4.

method sqlalchemy.engine.Connection.in_nested_transaction() bool

Возвращает True, если транзакция находится в процессе выполнения.

method sqlalchemy.engine.Connection.in_transaction() bool

Возвращает True, если транзакция находится в процессе выполнения.

attribute sqlalchemy.engine.Connection.info

Информационный словарь, связанный с базовым соединением DBAPI, на которое ссылается данный Connection, позволяющий ассоциировать с соединением данные, определяемые пользователем.

Данные здесь будут следовать вместе с соединением DBAPI, в том числе после возврата в пул соединений, и снова использоваться в последующих экземплярах Connection.

method sqlalchemy.engine.Connection.invalidate(exception: Optional[BaseException] = None) None

Аннулировать базовое соединение DBAPI, связанное с этим Connection.

Будет предпринята попытка немедленно закрыть базовое соединение DBAPI; однако если эта операция не удастся, ошибка будет зарегистрирована, но не поднята. Затем соединение будет разорвано независимо от того, удалось ли выполнить close() или нет.

При следующем использовании (где «использование» обычно означает использование метода Connection.execute() или аналогичного), этот Connection попытается получить новое соединение DBAPI, используя услуги Pool в качестве источника соединения (например, «повторное соединение»).

Если транзакция находилась в процессе выполнения (например, был вызван метод Connection.begin()), когда вызывается метод Connection.invalidate(), на уровне DBAPI все состояние, связанное с этой транзакцией, теряется, поскольку соединение DBAPI закрывается. Connection не позволит продолжить повторное соединение, пока объект Transaction не будет завершен вызовом метода Transaction.rollback(); до этого момента любая попытка продолжить использование Connection вызовет ошибку InvalidRequestError. Это сделано для того, чтобы предотвратить случайное продолжение транзакционных операций, несмотря на то, что транзакция была потеряна в результате аннулирования.

Метод Connection.invalidate(), как и автопроверка, на уровне пула соединений будет вызывать событие PoolEvents.invalidate().

Параметры:

exception – необязательный экземпляр Exception, который является причиной аннулирования. передается обработчикам событий и функциям протоколирования.

attribute sqlalchemy.engine.Connection.invalidated

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

Это не указывает на то, было ли соединение аннулировано на уровне пула, однако

method sqlalchemy.engine.Connection.rollback() None

Откатить транзакцию, которая в данный момент находится в процессе выполнения.

Этот метод откатывает текущую транзакцию, если она была начата. Если транзакция не была начата, метод не имеет эффекта. Если транзакция была начата и соединение находится в состоянии недействительности, транзакция очищается с помощью этого метода.

Транзакция начинается на Connection автоматически при первом выполнении оператора или при вызове метода Connection.begin().

Примечание

Метод Connection.rollback() действует только на первичную транзакцию базы данных, которая связана с объектом Connection. Он не действует на SAVEPOINT, который был бы вызван из метода Connection.begin_nested(); для управления SAVEPOINT вызовите NestedTransaction.rollback() на NestedTransaction, который возвращается самим методом Connection.begin_nested().

method sqlalchemy.engine.Connection.scalar(statement: Executable, parameters: Optional[_CoreSingleExecuteParams] = None, *, execution_options: Optional[CoreExecuteOptionsParameter] = None) Any

Выполняет конструкцию оператора SQL и возвращает скалярный объект.

Этот метод является сокращением для вызова метода Result.scalar() после вызова метода Connection.execute(). Параметры эквивалентны.

Результат:

скалярное значение Python, представляющее первый столбец первой возвращенной строки.

method sqlalchemy.engine.Connection.scalars(statement: Executable, parameters: Optional[_CoreAnyExecuteParams] = None, *, execution_options: Optional[CoreExecuteOptionsParameter] = None) ScalarResult[Any]

Выполняет и возвращает скалярный набор результатов, который дает скалярные значения из первого столбца каждой строки.

Этот метод эквивалентен вызову Connection.execute() для получения объекта Result, затем вызову метода Result.scalars() для создания экземпляра ScalarResult.

Результат:

a ScalarResult

Добавлено в версии 1.4.24.

method sqlalchemy.engine.Connection.schema_for_object(obj: HasSchemaAttr) Optional[str]

Возвращает имя схемы для данного элемента схемы с учетом текущей карты перевода схемы.

class sqlalchemy.engine.CreateEnginePlugin

Набор крючков, предназначенных для дополнения построения объекта Engine на основе имен точек входа в URL.

Цель CreateEnginePlugin - позволить сторонним системам применять слушателей событий на уровне движка, пула и диалекта без необходимости модификации целевого приложения; вместо этого имена плагинов могут быть добавлены в URL базы данных. Целевые приложения для CreateEnginePlugin включают:

  • инструменты производительности соединений и SQL, например, которые используют события для отслеживания количества проверок и/или времени, затраченного на выполнение утверждений

  • подключаемые модули, такие как прокси-серверы

Простейший CreateEnginePlugin, который присоединяет регистратор к объекту Engine может выглядеть следующим образом:

import logging

from sqlalchemy.engine import CreateEnginePlugin
from sqlalchemy import event

class LogCursorEventsPlugin(CreateEnginePlugin):
    def __init__(self, url, kwargs):
        # consume the parameter "log_cursor_logging_name" from the
        # URL query
        logging_name = url.query.get("log_cursor_logging_name", "log_cursor")

        self.log = logging.getLogger(logging_name)

    def update_url(self, url):
        "update the URL to one that no longer includes our parameters"
        return url.difference_update_query(["log_cursor_logging_name"])

    def engine_created(self, engine):
        "attach an event listener after the new Engine is constructed"
        event.listen(engine, "before_cursor_execute", self._log_event)


    def _log_event(
        self,
        conn,
        cursor,
        statement,
        parameters,
        context,
        executemany):

        self.log.info("Plugin logged cursor event: %s", statement)

Плагины регистрируются с помощью точек входа аналогично тому, как это делается для диалектов:

entry_points={
    'sqlalchemy.plugins': [
        'log_cursor_plugin = myapp.plugins:LogCursorEventsPlugin'
    ]

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

from sqlalchemy import create_engine

engine = create_engine(
    "mysql+pymysql://scott:tiger@localhost/test?"
    "plugin=log_cursor_plugin&log_cursor_logging_name=mylogger"
)

Параметр URL plugin поддерживает несколько экземпляров, поэтому в URL можно указать несколько плагинов; они загружаются в порядке, указанном в URL:

engine = create_engine(
  "mysql+pymysql://scott:tiger@localhost/test?"
  "plugin=plugin_one&plugin=plugin_twp&plugin=plugin_three")

Имена плагинов также могут быть переданы непосредственно в create_engine() с помощью аргумента create_engine.plugins:

engine = create_engine(
  "mysql+pymysql://scott:tiger@localhost/test",
  plugins=["myplugin"])

Добавлено в версии 1.2.3: Имена плагинов также могут быть указаны в create_engine() в виде списка

Плагин может потреблять специфические для плагина аргументы из объекта URL, а также из словаря kwargs, который является словарем аргументов, передаваемых вызову create_engine(). «Потребление» этих аргументов включает в себя то, что они должны быть удалены при инициализации плагина, чтобы аргументы не были переданы конструктору Dialect, где они вызовут ошибку ArgumentError, поскольку они не известны диалекту.

Начиная с версии 1.4 SQLAlchemy, аргументы следует продолжать получать из словаря kwargs непосредственно, удаляя значения с помощью метода типа dict.pop. Аргументы из объекта URL должны потребляться путем реализации метода CreateEnginePlugin.update_url(), возвращающего новую копию URL с удаленными специфическими для плагина параметрами:

class MyPlugin(CreateEnginePlugin):
    def __init__(self, url, kwargs):
        self.my_argument_one = url.query['my_argument_one']
        self.my_argument_two = url.query['my_argument_two']
        self.my_argument_three = kwargs.pop('my_argument_three', None)

    def update_url(self, url):
        return url.difference_update_query(
            ["my_argument_one", "my_argument_two"]
        )

Аргументы, подобные тем, что показаны выше, потребляются из вызова create_engine(), например:

from sqlalchemy import create_engine

engine = create_engine(
  "mysql+pymysql://scott:tiger@localhost/test?"
  "plugin=myplugin&my_argument_one=foo&my_argument_two=bar",
  my_argument_three='bat'
)

Изменено в версии 1.4: Объект URL теперь неизменяемый; CreateEnginePlugin, которому нужно изменить URL, должен реализовать новый добавленный метод CreateEnginePlugin.update_url(), который вызывается после создания плагина.

Для миграции постройте плагин следующим образом, проверяя существование метода CreateEnginePlugin.update_url(), чтобы определить, какая версия запущена:

class MyPlugin(CreateEnginePlugin):
    def __init__(self, url, kwargs):
        if hasattr(CreateEnginePlugin, "update_url"):
            # detect the 1.4 API
            self.my_argument_one = url.query['my_argument_one']
            self.my_argument_two = url.query['my_argument_two']
        else:
            # detect the 1.3 and earlier API - mutate the
            # URL directly
            self.my_argument_one = url.query.pop('my_argument_one')
            self.my_argument_two = url.query.pop('my_argument_two')

        self.my_argument_three = kwargs.pop('my_argument_three', None)

    def update_url(self, url):
        # this method is only called in the 1.4 version
        return url.difference_update_query(
            ["my_argument_one", "my_argument_two"]
        )

См.также

Объект URL теперь является неизменяемым - обзор изменения URL, который также включает заметки относительно CreateEnginePlugin.

Когда процесс создания движка завершается и создается объект Engine, он снова передается плагину через хук CreateEnginePlugin.engine_created(). В этом хуке в движок могут быть внесены дополнительные изменения, чаще всего связанные с настройкой событий (например, определенных в Основные мероприятия).

method sqlalchemy.engine.CreateEnginePlugin.__init__(url: URL, kwargs: Dict[str, Any])

Создайте новый CreateEnginePlugin.

Объект плагина инстанцируется отдельно для каждого вызова create_engine(). Единственный Engine будет передан методу CreateEnginePlugin.engine_created(), соответствующему данному URL.

Параметры:
  • url – объект URL. Плагин может проверять URL на наличие аргументов. Аргументы, используемые плагином, должны быть удалены, путем возврата обновленного URL из метода CreateEnginePlugin.update_url(). … versionchanged:: 1.4 Объект URL теперь неизменяемый, поэтому CreateEnginePlugin, которому нужно изменить объект URL, должен реализовать метод CreateEnginePlugin.update_url().

  • kwargs – Аргументы ключевых слов, переданные в create_engine().

method sqlalchemy.engine.CreateEnginePlugin.engine_created(engine: Engine) None

Получите объект Engine, когда он будет полностью построен.

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

method sqlalchemy.engine.CreateEnginePlugin.handle_dialect_kwargs(dialect_cls: Type[Dialect], dialect_args: Dict[str, Any]) None

разбор и модификация каргов диалекта

method sqlalchemy.engine.CreateEnginePlugin.handle_pool_kwargs(pool_cls: Type[Pool], pool_args: Dict[str, Any]) None

разбор и модификация каргов пула

method sqlalchemy.engine.CreateEnginePlugin.update_url(url: URL) URL

Обновите URL.

Должен быть возвращен новый URL. Этот метод обычно используется для потребления конфигурационных аргументов из URL, которые должны быть удалены, поскольку они не будут распознаны диалектом. Для удаления этих аргументов доступен метод URL.difference_update_query(). Пример см. в docstring по адресу CreateEnginePlugin.

Добавлено в версии 1.4.

class sqlalchemy.engine.Engine

Соединяет Pool и Dialect вместе, чтобы обеспечить источник подключения к базе данных и поведение.

Объект Engine инстанцируется публично с помощью функции create_engine().

Классная подпись

class sqlalchemy.engine.Engine (sqlalchemy.engine.interfaces.ConnectionEventsTarget, sqlalchemy.log.Identified, sqlalchemy.inspection.Inspectable)

method sqlalchemy.engine.Engine.begin() Iterator[Connection]

Возвращает менеджер контекста, доставляющий Connection с установленным Transaction.

Например:

with engine.begin() as conn:
    conn.execute(
        text("insert into table (x, y, z) values (1, 2, 3)")
    )
    conn.execute(text("my_special_procedure(5)"))

При успешном выполнении операция Transaction фиксируется. Если возникла ошибка, Transaction откатывается.

См.также

Engine.connect() - получить Connection от Engine.

Connection.begin() - начать Transaction для определенного Connection.

method sqlalchemy.engine.Engine.clear_compiled_cache() None

Очистить скомпилированный кэш, связанный с диалектом.

Это относится только к встроенному кэшу, который устанавливается через параметр create_engine.query_cache_size. Это не влияет на кэши словарей, которые были переданы через параметр Connection.execution_options.query_cache.

Добавлено в версии 1.4.

method sqlalchemy.engine.Engine.connect() Connection

Возвращает новый объект Connection.

Connection действует как менеджер контекста Python, поэтому типичное использование этого метода выглядит так:

with engine.connect() as connection:
    connection.execute(text("insert into table values ('foo')"))
    connection.commit()

Как указано выше, после завершения блока соединение «закрывается», а его базовые ресурсы DBAPI возвращаются в пул соединений. Это также имеет эффект отката любой транзакции, которая была начата явно или была начата через autobegin, и вызовет событие ConnectionEvents.rollback(), если транзакция была начата и все еще находится в процессе.

См.также

Engine.begin()

method sqlalchemy.engine.Engine.dispose(close: bool = True) None

Утилизация пула соединений, используемого этим Engine.

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

Параметры:

close – если оставить значение по умолчанию True, это приведет к полному закрытию всех текущих проверенных соединений базы данных. Соединения, которые все еще проверяются, **не будут закрыты, однако они больше не будут связаны с этим Engine, поэтому, когда они закрываются по отдельности, в конечном итоге Pool, с которым они связаны, будет собран мусор, и они будут полностью закрыты, если еще не были закрыты при регистрации. Если установлено значение False, предыдущий пул соединений отменяется, а в остальном он никак не затрагивается.

Добавлено в версии 1.4.33: Добавлен параметр Engine.dispose.close, позволяющий заменить пул соединений в дочернем процессе без вмешательства в соединения, используемые родительским процессом.

attribute sqlalchemy.engine.Engine.driver

Имя драйвера Dialect, используемого данным Engine.

attribute sqlalchemy.engine.Engine.engine

Возвращает это Engine.

Используется для устаревших схем, которые принимают объекты Connection / Engine в пределах одной переменной.

method sqlalchemy.engine.Engine.execution_options(**opt: Any) OptionEngine

Возвращает новый Engine, который будет предоставлять Connection объекты с заданными параметрами выполнения.

Возвращенный Engine остается связанным с исходным Engine в том смысле, что он использует тот же пул соединений и другие состояния:

  • Pool, используемый новым Engine, является тем же экземпляром. Метод Engine.dispose() заменит экземпляр пула соединений для родительского движка, а также этот экземпляр.

  • Слушатели событий «каскадируются» - это означает, что новый Engine наследует события родителя, а новые события могут быть связаны с новым Engine индивидуально.

  • Конфигурация протоколирования и имя logging_name копируется из родительского Engine.

Смысл метода Engine.execution_options() заключается в реализации схем, в которых несколько объектов Engine ссылаются на один и тот же пул соединений, но различаются опциями, которые влияют на некоторое поведение на уровне выполнения для каждого механизма. Одним из таких примеров является разбиение на отдельные экземпляры «читатель» и «писатель» Engine, где один Engine имеет более низкие настройки isolation level или даже отключен от транзакций с помощью «autocommit». Пример такой конфигурации приведен в Поддержание нескольких уровней изоляции для одного двигателя.

Другой пример использует пользовательскую опцию shard_id, которая потребляется событием для изменения текущей схемы на соединении с базой данных:

from sqlalchemy import event
from sqlalchemy.engine import Engine

primary_engine = create_engine("mysql+mysqldb://")
shard1 = primary_engine.execution_options(shard_id="shard1")
shard2 = primary_engine.execution_options(shard_id="shard2")

shards = {"default": "base", "shard_1": "db1", "shard_2": "db2"}

@event.listens_for(Engine, "before_cursor_execute")
def _switch_shard(conn, cursor, stmt,
        params, context, executemany):
    shard_id = conn.get_execution_options().get('shard_id', "default")
    current_shard = conn.info.get("current_shard", None)

    if current_shard != shard_id:
        cursor.execute("use %s" % shards[shard_id])
        conn.info["current_shard"] = shard_id

Приведенный выше рецепт иллюстрирует два объекта Engine, каждый из которых будет служить фабрикой для объектов Connection, в которых присутствуют заранее установленные опции выполнения «shard_id». Обработчик событий ConnectionEvents.before_cursor_execute() интерпретирует эту опцию выполнения, чтобы выдать оператор MySQL use для переключения баз данных перед выполнением оператора, в то же время отслеживая, какую базу данных мы установили с помощью словаря Connection.info.

См.также

Connection.execution_options() - обновить параметры выполнения для объекта Connection.

Engine.update_execution_options() - обновить параметры выполнения для данного Engine на месте.

Engine.get_execution_options()

method sqlalchemy.engine.Engine.get_execution_options() _ExecuteOptions

Получить параметры, не относящиеся к SQL, которые будут действовать во время выполнения.

См.также

Engine.execution_options()

attribute sqlalchemy.engine.Engine.name

Строковое имя Dialect, используемое этим Engine.

method sqlalchemy.engine.Engine.raw_connection() PoolProxiedConnection

Возвращает «сырое» соединение DBAPI из пула соединений.

Возвращаемый объект представляет собой проксированную версию объекта соединения DBAPI, используемого используемым базовым драйвером. Объект будет иметь все то же поведение, что и реальное соединение DBAPI, за исключением того, что его метод close() приведет к возврату соединения в пул, а не к реальному закрытию.

Этот метод обеспечивает прямой доступ к соединению DBAPI для особых ситуаций, когда API, предоставляемый Connection, не нужен. Когда объект Connection уже присутствует, соединение DBAPI доступно с помощью аксессора Connection.connection.

method sqlalchemy.engine.Engine.update_execution_options(**opt: Any) None

Обновление словаря параметров выполнения по умолчанию этого Engine.

Заданные ключи/значения в **opt добавляются к опциям выполнения по умолчанию, которые будут использоваться для всех соединений. Начальное содержимое этого словаря может быть отправлено через параметр execution_options в create_engine().

class sqlalchemy.engine.ExceptionContext

Инкапсулировать информацию о текущем состоянии ошибки.

Этот объект существует исключительно для передачи в событие DialectEvents.handle_error(), поддерживая интерфейс, который может быть расширен без обратной несовместимости.

attribute sqlalchemy.engine.ExceptionContext.chained_exception: Optional[BaseException]

Исключение, которое было возвращено предыдущим обработчиком в цепочке исключений, если таковое имеется.

Если оно присутствует, то это исключение будет в конечном итоге вызвано SQLAlchemy, если последующий обработчик не заменит его.

Может отсутствовать.

attribute sqlalchemy.engine.ExceptionContext.connection: Optional[Connection]

Connection, используемый во время исключения.

Этот член присутствует, за исключением случаев сбоя при первом подключении.

См.также

ExceptionContext.engine

attribute sqlalchemy.engine.ExceptionContext.cursor: Optional[DBAPICursor]

Объект курсора DBAPI.

Может отсутствовать.

attribute sqlalchemy.engine.ExceptionContext.dialect: Dialect

Используемый Dialect.

Этот член присутствует во всех вызовах крючка событий.

Добавлено в версии 2.0.

attribute sqlalchemy.engine.ExceptionContext.engine: Optional[Engine]

Engine, используемый во время исключения.

Этот член присутствует во всех случаях, кроме случаев обработки ошибки в процессе «pre-ping» пула соединений.

attribute sqlalchemy.engine.ExceptionContext.execution_context: Optional[ExecutionContext]

ExecutionContext, соответствующий выполняемой операции.

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

Обратите внимание, что члены ExceptionContext.statement и ExceptionContext.parameters могут представлять значение, отличное от значения ExecutionContext, потенциально в случае, когда событие ConnectionEvents.before_cursor_execute() или подобное изменило заявление/параметры для отправки.

Может отсутствовать.

attribute sqlalchemy.engine.ExceptionContext.invalidate_pool_on_disconnect: bool

Представляет, должны ли все соединения в пуле быть аннулированы, когда действует условие «разъединение».

Установка этого флага в False в рамках события DialectEvents.handle_error() приведет к тому, что вся коллекция соединений в пуле не будет аннулирована во время разъединения; только текущее соединение, которое является предметом ошибки, будет фактически аннулировано.

Этот флаг предназначен для пользовательских схем обработки разъединений, когда аннулирование других соединений в пуле должно выполняться на основе других условий или даже на основе каждого соединения.

attribute sqlalchemy.engine.ExceptionContext.is_disconnect: bool

Отражает, представляет ли возникшее исключение условие «отключения».

Этот флаг всегда будет True или False в пределах области действия обработчика DialectEvents.handle_error().

SQLAlchemy будет использовать этот флаг, чтобы определить, следует ли впоследствии аннулировать соединение. То есть, присвоив этот флаг, можно вызвать событие «разъединение», которое впоследствии приведет к аннулированию соединения и пула, или предотвратить его, изменив этот флаг.

Примечание

Обработчик «pre_ping» пула, включенный с помощью параметра create_engine.pool_pre_ping, не обращается к этому событию, прежде чем решить, что «ping» вернул false, в отличие от получения необработанной ошибки. Для данного случая использования используется параметр legacy recipe based on engine_connect() may be used. Будущий API позволит более полно настроить механизм обнаружения «разъединения» для всех функций.

attribute sqlalchemy.engine.ExceptionContext.is_pre_ping: bool

Указывает, происходит ли эта ошибка в рамках шага «pre-ping», выполняемого, когда create_engine.pool_pre_ping установлен в True. В этом режиме атрибут ExceptionContext.engine будет None. Используемый диалект доступен через атрибут ExceptionContext.dialect.

Добавлено в версии 2.0.5.

attribute sqlalchemy.engine.ExceptionContext.original_exception: BaseException

Объект исключения, которое было поймано.

Этот член присутствует всегда.

attribute sqlalchemy.engine.ExceptionContext.parameters: Optional[_DBAPIAnyExecuteParams]

Коллекция параметров, которая была передана непосредственно в DBAPI.

Может отсутствовать.

attribute sqlalchemy.engine.ExceptionContext.sqlalchemy_exception: Optional[StatementError]

sqlalchemy.exc.StatementError, который обертывает оригинал и будет поднят, если обработка исключений не будет обойдена событием.

Может быть None, так как не все типы исключений оборачиваются SQLAlchemy. Для исключений на уровне DBAPI, которые подклассифицируют класс Error от dbapi, это поле всегда будет присутствовать.

attribute sqlalchemy.engine.ExceptionContext.statement: Optional[str]

Строковый SQL-запрос, который был передан непосредственно в DBAPI.

Может отсутствовать.

class sqlalchemy.engine.NestedTransaction

Представляют собой «вложенную», или SAVEPOINT транзакцию.

Объект NestedTransaction создается путем вызова метода Connection.begin_nested() из Connection.

При использовании NestedTransaction семантика «begin» / «commit» / «rollback» следующая:

  • операция «begin» соответствует команде «BEGIN SAVEPOINT», где точке сохранения присваивается явное имя, которое является частью состояния этого объекта.

  • Метод NestedTransaction.commit() соответствует операции «RELEASE SAVEPOINT», используя идентификатор точки сохранения, связанный с данным NestedTransaction.

  • Метод NestedTransaction.rollback() соответствует операции «ROLLBACK TO SAVEPOINT», используя идентификатор точки сохранения, связанный с данным NestedTransaction.

Обоснование для имитации семантики внешней транзакции в терминах точек сохранения, чтобы код мог работать с транзакцией «точки сохранения» и «внешней» транзакцией независимо друг от друга.

См.также

Использование SAVEPOINT - ORM-версия API SAVEPOINT.

Классная подпись

класс sqlalchemy.engine.NestedTransaction (sqlalchemy.engine.Transaction)

method sqlalchemy.engine.NestedTransaction.close() None

наследуется от Transaction.close() метода Transaction

Закройте это Transaction.

Если эта транзакция является базовой транзакцией во вложении begin/commit, транзакция откатывается назад(). В противном случае метод возвращается.

Используется для отмены транзакции, не затрагивая область действия входящей в нее транзакции.

method sqlalchemy.engine.NestedTransaction.commit() None

наследуется от Transaction.commit() метода Transaction

Зафиксируйте это Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует COMMIT.

  • Для NestedTransaction это соответствует операции «RELEASE SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

method sqlalchemy.engine.NestedTransaction.rollback() None

наследуется от Transaction.rollback() метода Transaction

Откатитесь назад Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует ROLLBACK.

  • Для NestedTransaction это соответствует операции «ROLLBACK TO SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

class sqlalchemy.engine.RootTransaction

Представьте «корневую» транзакцию на Connection.

Это соответствует текущему «BEGIN/COMMIT/ROLLBACK», который происходит для Connection. RootTransaction создается вызовом метода Connection.begin() и остается связанным с Connection на протяжении всего периода его активности. Текущий используемый RootTransaction доступен через метод Connection.get_transaction в Connection.

При использовании 2.0 style в Connection также применяется поведение «автозапуска», которое будет создавать новый RootTransaction всякий раз, когда соединение в нетранзакционном состоянии используется для передачи команд на соединение DBAPI. Область применения RootTransaction в стиле 2.0 можно контролировать с помощью методов Connection.commit() и Connection.rollback().

Классная подпись

класс sqlalchemy.engine.RootTransaction (sqlalchemy.engine.Transaction)

method sqlalchemy.engine.RootTransaction.close() None

наследуется от Transaction.close() метода Transaction

Закройте это Transaction.

Если эта транзакция является базовой транзакцией во вложении begin/commit, транзакция откатывается назад(). В противном случае метод возвращается.

Используется для отмены транзакции, не затрагивая область действия входящей в нее транзакции.

method sqlalchemy.engine.RootTransaction.commit() None

наследуется от Transaction.commit() метода Transaction

Зафиксируйте это Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует COMMIT.

  • Для NestedTransaction это соответствует операции «RELEASE SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

method sqlalchemy.engine.RootTransaction.rollback() None

наследуется от Transaction.rollback() метода Transaction

Откатитесь назад Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует ROLLBACK.

  • Для NestedTransaction это соответствует операции «ROLLBACK TO SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

class sqlalchemy.engine.Transaction

Представляет собой транзакцию базы данных, находящуюся в процессе выполнения.

Объект Transaction приобретается путем вызова метода Connection.begin() из Connection:

from sqlalchemy import create_engine
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")
connection = engine.connect()
trans = connection.begin()
connection.execute(text("insert into x (a, b) values (1, 2)"))
trans.commit()

Объект предоставляет методы rollback() и commit() для управления границами транзакций. Он также реализует интерфейс менеджера контекста, чтобы оператор Python with можно было использовать с методом Connection.begin():

with connection.begin():
    connection.execute(text("insert into x (a, b) values (1, 2)"))

Объект Transaction является не потокобезопасным.

Классная подпись

класс sqlalchemy.engine.Transaction (sqlalchemy.engine.util.TransactionalContext)

method sqlalchemy.engine.Transaction.close() None

Закройте это Transaction.

Если эта транзакция является базовой транзакцией во вложении begin/commit, транзакция откатывается назад(). В противном случае метод возвращается.

Используется для отмены транзакции, не затрагивая область действия входящей в нее транзакции.

method sqlalchemy.engine.Transaction.commit() None

Зафиксируйте это Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует COMMIT.

  • Для NestedTransaction это соответствует операции «RELEASE SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

method sqlalchemy.engine.Transaction.rollback() None

Откатитесь назад Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует ROLLBACK.

  • Для NestedTransaction это соответствует операции «ROLLBACK TO SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

class sqlalchemy.engine.TwoPhaseTransaction

Представляют собой двухфазную транзакцию.

Новый объект TwoPhaseTransaction может быть получен с помощью метода Connection.begin_twophase().

Интерфейс такой же, как у Transaction, с добавлением метода prepare().

Классная подпись

класс sqlalchemy.engine.TwoPhaseTransaction (sqlalchemy.engine.RootTransaction)

method sqlalchemy.engine.TwoPhaseTransaction.close() None

наследуется от Transaction.close() метода Transaction

Закройте это Transaction.

Если эта транзакция является базовой транзакцией во вложении begin/commit, транзакция откатывается назад(). В противном случае метод возвращается.

Используется для отмены транзакции, не затрагивая область действия входящей в нее транзакции.

method sqlalchemy.engine.TwoPhaseTransaction.commit() None

наследуется от Transaction.commit() метода Transaction

Зафиксируйте это Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует COMMIT.

  • Для NestedTransaction это соответствует операции «RELEASE SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

method sqlalchemy.engine.TwoPhaseTransaction.prepare() None

Подготовьте это TwoPhaseTransaction.

После PREPARE транзакция может быть зафиксирована.

method sqlalchemy.engine.TwoPhaseTransaction.rollback() None

наследуется от Transaction.rollback() метода Transaction

Откатитесь назад Transaction.

Реализация этого может варьироваться в зависимости от типа используемой транзакции:

  • Для простой транзакции базы данных (например, RootTransaction) это соответствует ROLLBACK.

  • Для NestedTransaction это соответствует операции «ROLLBACK TO SAVEPOINT».

  • Для TwoPhaseTransaction могут быть использованы методы, специфичные для DBAPI для двухфазных транзакций.

API набора результатов

Object Name Description

ChunkedIteratorResult

IteratorResult, который работает от вызываемого итератора.

CursorResult

Результат, представляющий состояние курсора DBAPI.

FilterResult

Обертка для Result, которая возвращает объекты, отличные от Row, такие как словари или скалярные объекты.

FrozenResult

Представляет объект Result в «замороженном» состоянии, пригодном для кэширования.

IteratorResult

Result, который получает данные из итератора Python объектов Row или аналогичных строкоподобных данных.

MappingResult

Обертка для Result, которая возвращает словарные значения, а не значения Row.

MergedResult

Объект Result, который объединяется из любого количества объектов Result.

Result

Представляют собой набор результатов базы данных.

Row

Представляют собой одну строку результата.

RowMapping

Mapping, который сопоставляет имена столбцов и объектов со значениями Row.

ScalarResult

Обертка для Result, которая возвращает скалярные значения, а не значения Row.

TupleResult

Result, который типизирован как возвращающий обычные кортежи Python вместо строк.

class sqlalchemy.engine.ChunkedIteratorResult

IteratorResult, который работает от вызываемого итератора.

Данный аргумент chunks является функцией, которой задается количество строк, возвращаемых в каждом чанке, или None для всех строк. Функция должна вернуть непотребляемый итератор списков, каждый список запрашиваемого размера.

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

Добавлено в версии 1.4.

Members

yield_per()

Классная подпись

класс sqlalchemy.engine.ChunkedIteratorResult (sqlalchemy.engine.IteratorResult)

method sqlalchemy.engine.ChunkedIteratorResult.yield_per(num: int) Self

Настройте стратегию выборки строк на выборку num строк за один раз.

Это влияет на базовое поведение результата при итерации по объекту результата или при использовании таких методов, как Result.fetchone(), которые возвращают по одной строке за раз. Данные из базового курсора или другого источника данных будут буферизироваться в памяти до этого количества строк, а затем буферизированная коллекция будет выдаваться по одной строке за раз или столько строк, сколько запрошено. Каждый раз, когда буфер очищается, он обновляется до этого количества строк или столько, сколько строк осталось, если осталось меньше.

Метод Result.yield_per() обычно используется в сочетании с опцией выполнения Connection.execution_options.stream_results, которая позволит используемому диалекту базы данных использовать курсор на стороне сервера, если DBAPI поддерживает определенный режим «курсора на стороне сервера» отдельно от режима работы по умолчанию.

Совет

Рассмотрите возможность использования опции выполнения Connection.execution_options.yield_per, которая одновременно установит Connection.execution_options.stream_results для обеспечения использования курсоров на стороне сервера, а также автоматически вызовет метод Result.yield_per(), чтобы сразу установить фиксированный размер буфера строк.

Опция выполнения Connection.execution_options.yield_per доступна для операций ORM, а использование , ориентированное на Session, описано в Получение больших наборов результатов с доходностью за. Версия только для Core, работающая с Connection, является новой в SQLAlchemy 1.4.40.

Добавлено в версии 1.4.

Параметры:

num – количество строк для выборки при каждом пополнении буфера. Если задано значение меньше 1, выполняется выборка всех строк для следующего буфера.

class sqlalchemy.engine.CursorResult

Результат, представляющий состояние курсора DBAPI.

Изменено в версии 1.4: Класс CursorResult` заменяет предыдущий интерфейс ResultProxy. Эти классы основаны на API вызова Result, который предоставляет обновленную модель использования и фасад вызова для SQLAlchemy Core и SQLAlchemy ORM.

Возвращает строки базы данных с помощью класса Row, который предоставляет дополнительные возможности API и поведение поверх необработанных данных, возвращаемых DBAPI. С помощью фильтров, таких как метод Result.scalars(), могут быть возвращены и другие типы объектов.

См.также

Использование операторов SELECT - вводный материал для доступа к объектам CursorResult и Row.

Классная подпись

класс sqlalchemy.engine.CursorResult (sqlalchemy.engine.Result)

method sqlalchemy.engine.CursorResult.all() Sequence[Row[_TP]]

наследуется от Result.all() метода Result

Возвращает все строки в списке.

Закрывает набор результатов после вызова. Последующие вызовы будут возвращать пустой список.

Добавлено в версии 1.4.

Результат:

список объектов Row.

method sqlalchemy.engine.CursorResult.close() Any

Закройте это CursorResult.

Это закрывает курсор DBAPI, соответствующий выполнению оператора, если он еще присутствует. Обратите внимание, что курсор DBAPI автоматически освобождается, когда CursorResult исчерпывает все доступные строки. CursorResult.close() является необязательным методом, за исключением случаев, когда отбрасывается CursorResult, в котором еще есть дополнительные строки, ожидающие выборки.

После вызова этого метода уже нельзя обращаться к методам fetch, которые при последующем использовании будут вызывать ошибку ResourceClosedError.

method sqlalchemy.engine.CursorResult.columns(*col_expressions: _KeyIndexType) Self

наследуется от Result.columns() метода Result

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

Этот метод можно использовать для ограничения возвращаемых столбцов, а также для их переупорядочивания. Заданный список выражений обычно представляет собой ряд целых чисел или строковых имен ключей. Это также могут быть соответствующие объекты ColumnElement, которые соответствуют заданной конструкции оператора.

Изменено в версии 2.0: Из-за ошибки в версии 1.4 метод Result.columns() имел некорректное поведение, когда вызов метода с одним индексом приводил к тому, что объект Result выдавал скалярные значения, а не объекты Row. В версии 2.0 это поведение было исправлено, и теперь вызов метода Result.columns() с одним индексом приводит к созданию объекта Result, который продолжает выдавать объекты Row, включающие только один столбец.

Например:

statement = select(table.c.x, table.c.y, table.c.z)
result = connection.execute(statement)

for z, y in result.columns('z', 'y'):
    # ...

Пример использования объектов столбцов из самого оператора:

for z, y in result.columns(
        statement.selected_columns.c.z,
        statement.selected_columns.c.y
):
    # ...

Добавлено в версии 1.4.

Параметры:

*col_expressions – указывает на возвращаемые столбцы. Элементами могут быть целочисленные индексы строк, строковые имена столбцов или соответствующие объекты ColumnElement, соответствующие конструкции select.

Результат:

этот объект Result с заданными модификациями.

method sqlalchemy.engine.CursorResult.fetchall() Sequence[Row[_TP]]

наследуется от Result.fetchall() метода Result

Синоним метода Result.all().

method sqlalchemy.engine.CursorResult.fetchmany(size: Optional[int] = None) Sequence[Row[_TP]]

наследуется от Result.fetchmany() метода Result

Получение большого количества строк.

Когда все строки исчерпаны, возвращает пустой список.

Этот метод предусмотрен для обратной совместимости с SQLAlchemy 1.x.x.

Чтобы получать строки группами, используйте метод Result.partitions().

Результат:

список объектов Row.

См.также

Result.partitions()

method sqlalchemy.engine.CursorResult.fetchone() Optional[Row[_TP]]

наследуется от Result.fetchone() метода Result

Получение одного ряда.

Если все строки исчерпаны, возвращается None.

Этот метод предусмотрен для обратной совместимости с SQLAlchemy 1.x.x.

Чтобы получить только первую строку результата, используйте метод Result.first(). Для итерации по всем строкам используйте непосредственно объект Result.

Результат:

объект Row, если фильтры не применяются, или None, если не осталось ни одной строки.

method sqlalchemy.engine.CursorResult.first() Optional[Row[_TP]]

наследуется от Result.first() метода Result

Получить первую строку или None, если строка отсутствует.

Закрывает набор результатов и отбрасывает оставшиеся строки.

Примечание

По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar() или комбинируйте Result.scalars() и Result.first().

Кроме того, в отличие от поведения унаследованного метода ORM Query.first(), не применяется ограничение к SQL-запросу, который был вызван для создания этого Result; для драйвера DBAPI, который буферизирует результаты в памяти перед выдачей строк, все строки будут отправлены в процесс Python, и все, кроме первой строки, будут отброшены.

Результат:

объект Row, или None, если не осталось строк.

См.также

Result.scalar()

Result.one()

method sqlalchemy.engine.CursorResult.freeze() FrozenResult[_TP]

наследуется от Result.freeze() метода Result

Возвращает вызываемый объект, который при вызове будет создавать копии этого Result.

Возвращаемый вызываемый объект является экземпляром FrozenResult.

Используется для кэширования набора результатов. Метод должен быть вызван на результате, когда он не был поглощен, и вызов метода полностью поглотит результат. Когда FrozenResult извлекается из кэша, его можно вызывать любое количество раз, и каждый раз он будет создавать новый объект Result по сохраненному набору строк.

См.также

Повторное выполнение заявлений - пример использования в ORM для реализации кэша наборов результатов.

attribute sqlalchemy.engine.CursorResult.inserted_primary_key

Возвращает первичный ключ для только что вставленной строки.

Возвращаемым значением является объект Row, представляющий именованный кортеж значений первичного ключа в том порядке, в котором столбцы первичного ключа настроены в источнике Table.

Изменено в версии 1.4.8: - the CursorResult.inserted_primary_key value is now a named tuple via the Row class, rather than a plain tuple.

Этот аксессор применяется только к однорядным конструкциям insert(), в которых не было явно указано Insert.returning(). Поддержка многорядных вставок, пока еще недоступная для большинства бэкендов, будет доступна с помощью аксессора CursorResult.inserted_primary_key_rows.

Обратите внимание, что столбцы первичного ключа, в которых указано условие server_default или которые иначе не квалифицируются как «автоинкрементные» столбцы (см. примечания в Column), и которые были созданы с использованием значения по умолчанию на стороне базы данных, будут отображаться в этом списке как None, если бэкенд не поддерживает «возврат» и оператор вставки выполняется с включенным «неявным возвратом».

Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией insert().

attribute sqlalchemy.engine.CursorResult.inserted_primary_key_rows

Возвращает значение CursorResult.inserted_primary_key в виде строки, содержащейся в списке; некоторые диалекты могут поддерживать и многострочную форму.

Примечание

Как указано ниже, в текущих версиях SQLAlchemy этот аксессор полезен только в дополнение к тому, что уже предоставляется CursorResult.inserted_primary_key при использовании диалекта psycopg2. Будущие версии надеются распространить эту возможность на большее количество диалектов.

Этот аксессор добавлен для поддержки диалектов, которые предлагают функцию, которая в настоящее время реализована функцией Помощники быстрого выполнения Psycopg2, в настоящее время только в диалекте psycopg2, которая обеспечивает одновременную INSERT многих строк, сохраняя при этом возможность возвращать значения первичного ключа, сгенерированные сервером.

  • При использовании диалекта psycopg2 или других диалектов, которые могут поддерживать вставки в стиле «fast executemany» в ближайших релизах : При вызове оператора INSERT с передачей списка строк в качестве второго аргумента в Connection.execute(), этот аксессор будет предоставлять список строк, где каждая строка содержит значение первичного ключа для каждой строки, которая была вставлена.

  • При использовании всех других диалектов / бэкендов, которые еще не поддерживают эту функцию: Этот аксессор полезен только для однорядных операторов INSERT, и возвращает ту же информацию, что и CursorResult.inserted_primary_key в одноэлементном списке. Когда оператор INSERT выполняется в сочетании со списком вставляемых строк, список будет содержать по одной строке на каждую строку, вставленную в оператор, однако он будет содержать None для любых генерируемых сервером значений.

В будущих выпусках SQLAlchemy функция psycopg2 «помощник быстрого выполнения» будет обобщена для других диалектов, что позволит использовать этот аксессор более широко.

Добавлено в версии 1.4.

attribute sqlalchemy.engine.CursorResult.is_insert

True, если этот CursorResult является результатом выполнения скомпилированной конструкции insert() языка выражений.

Если значение True, это означает, что атрибут inserted_primary_key доступен, при условии, что утверждение не включает определенную пользователем конструкцию «returning».

method sqlalchemy.engine.CursorResult.keys() RMKeyView

наследуется от sqlalchemy.engine._WithKeys.keys метода sqlalchemy.engine._WithKeys

Возвращает итерируемое представление, которое дает ключи строк, которые были бы представлены каждым Row.

Ключи могут представлять собой метки столбцов, возвращаемых оператором core, или имена классов orm, возвращаемых выполнением orm.

Представление также можно проверить на содержание ключей с помощью оператора Python in, который будет проверять как строковые ключи, представленные в представлении, так и альтернативные ключи, такие как объекты столбцов.

Изменено в версии 1.4: возвращается объект представления ключа, а не обычный список.

method sqlalchemy.engine.CursorResult.last_inserted_params()

Возвращает коллекцию вставленных параметров из этого выполнения.

Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией insert().

method sqlalchemy.engine.CursorResult.last_updated_params()

Возвращает коллекцию обновленных параметров из этого выполнения.

Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией update().

method sqlalchemy.engine.CursorResult.lastrow_has_defaults()

Возврат lastrow_has_defaults() из базового ExecutionContext.

Подробнее см. в разделе ExecutionContext.

attribute sqlalchemy.engine.CursorResult.lastrowid

Возвращает аксессор „lastrowid“ курсора DBAPI.

Это специфический метод DBAPI, и он работает только для тех бэкендов, которые его поддерживают, для утверждений, где он уместен. Его поведение не является последовательным для разных бэкендов.

Использование этого метода обычно не требуется при использовании конструкций выражения insert(); атрибут CursorResult.inserted_primary_key предоставляет кортеж значений первичного ключа для вновь вставленной строки, независимо от бэкенда базы данных.

method sqlalchemy.engine.CursorResult.mappings() MappingResult

наследуется от Result.mappings() метода Result

Применить фильтр сопоставлений к возвращаемым строкам, возвращая экземпляр MappingResult.

Когда применяется этот фильтр, при выборке строк будут возвращаться объекты RowMapping, а не Row.

Добавлено в версии 1.4.

Результат:

новый объект фильтрации MappingResult, ссылающийся на данный объект Result.

method sqlalchemy.engine.CursorResult.merge(*others: Result[Any]) MergedResult[Any]

Объедините этот Result с другими совместимыми объектами результата.

Возвращаемый объект является экземпляром MergedResult, который будет состоять из итераторов от заданных объектов результата.

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

method sqlalchemy.engine.CursorResult.one() Row[_TP]

наследуется от Result.one() метода Result

Вернуть ровно одну строку или выдать исключение.

Вызывает NoResultFound, если результат не возвращает ни одной строки, или MultipleResultsFound, если будет возвращено несколько строк.

Примечание

По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar_one() или комбинируйте Result.scalars() и Result.one().

Добавлено в версии 1.4.

Результат:

Первый Row.

поднимает:

MultipleResultsFound, NoResultFound

method sqlalchemy.engine.CursorResult.one_or_none() Optional[Row[_TP]]

наследуется от Result.one_or_none() метода Result

Возвращает не более одного результата или выдает исключение.

Возвращает None, если результат не содержит строк. Возвращает MultipleResultsFound, если возвращается несколько строк.

Добавлено в версии 1.4.

Результат:

Первая строка Row или None, если строка отсутствует.

поднимает:

MultipleResultsFound

См.также

Result.first()

Result.one()

method sqlalchemy.engine.CursorResult.partitions(size: Optional[int] = None) Iterator[Sequence[Row[_TP]]]

наследуется от Result.partitions() метода Result

Итерация по подспискам строк заданного размера.

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

Объект result автоматически закрывается, когда итератор полностью израсходован.

Обратите внимание, что драйвер бэкенда обычно буферизирует весь результат заранее, если только не используется опция выполнения Connection.execution_options.stream_results, указывающая, что драйвер не должен предварительно буферизировать результаты, если это возможно. Не все драйверы поддерживают эту опцию, и для тех, кто ее не поддерживает, опция игнорируется.

При использовании ORM метод Result.partitions() обычно более эффективен с точки зрения памяти, когда он сочетается с использованием yield_per execution option, который инструктирует как драйвер DBAPI использовать курсоры на стороне сервера, если они доступны, так и инструктирует внутренние механизмы загрузки ORM создавать только определенное количество объектов ORM из результата за один раз, прежде чем выдавать их.

Добавлено в версии 1.4.

Параметры:

size – указывает максимальное количество строк, которое должно присутствовать в каждом выдаваемом списке. Если None, используется значение, установленное методом Result.yield_per(), если он был вызван, или опция выполнения Connection.execution_options.yield_per, которая эквивалентна в этом отношении. Если yield_per не задан, то используется значение по умолчанию Result.fetchmany(), которое может быть специфичным для бэкенда и не вполне определенным.

Результат:

итератор списков

method sqlalchemy.engine.CursorResult.postfetch_cols()

Возврат postfetch_cols() из базового ExecutionContext.

Подробнее см. в разделе ExecutionContext.

Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией insert() или update().

method sqlalchemy.engine.CursorResult.prefetch_cols()

Возврат prefetch_cols() из базового ExecutionContext.

Подробнее см. в разделе ExecutionContext.

Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией insert() или update().

attribute sqlalchemy.engine.CursorResult.returned_defaults

Возвращает значения столбцов по умолчанию, которые были получены с помощью функции ValuesBase.return_defaults().

Значением является экземпляр Row, или None, если ValuesBase.return_defaults() не был использован или если бэкенд не поддерживает RETURNING.

См.также

ValuesBase.return_defaults()

attribute sqlalchemy.engine.CursorResult.returned_defaults_rows

Возвращает список строк, каждая из которых содержит значения столбцов по умолчанию, которые были получены с помощью функции ValuesBase.return_defaults().

Возвращаемое значение - список объектов Row.

Добавлено в версии 1.4.

attribute sqlalchemy.engine.CursorResult.returns_rows

True, если этот CursorResult возвращает ноль или более строк.

Т.е. законно ли вызывать методы CursorResult.fetchone(), CursorResult.fetchmany() CursorResult.fetchall().

В целом, значение CursorResult.returns_rows всегда должно быть синонимом того, имел ли курсор DBAPI атрибут .description, указывающий на наличие колонок результатов, при этом следует отметить, что курсор, возвращающий ноль строк, все равно имеет .description, если был выпущен оператор возврата строк.

Этот атрибут должен иметь значение True для всех результатов, которые относятся к операторам SELECT, а также для DML-операторов INSERT/UPDATE/DELETE, которые используют RETURNING. Для операторов INSERT/UPDATE/DELETE, не использующих RETURNING, значение обычно будет False, однако существуют некоторые специфические для диалекта исключения, например, при использовании диалекта MSSQL / pyodbc SELECT выдается inline для получения вставленного значения первичного ключа.

attribute sqlalchemy.engine.CursorResult.rowcount

Возвращает „rowcount“ для этого результата.

Счетчик строк» сообщает о количестве строк, сопоставленных по критерию WHERE в операторе UPDATE или DELETE.

Примечание

Заметки относительно CursorResult.rowcount:

  • Этот атрибут возвращает количество строк сопоставленных, что не обязательно совпадает с количеством строк, которые были фактически изменены - например, оператор UPDATE может не иметь никаких чистых изменений в данной строке, если заданные значения SET совпадают с теми, которые уже присутствуют в строке. Такой ряд будет сопоставлен, но не изменен. В бэкендах, поддерживающих оба стиля, таких как MySQL, rowcount по умолчанию настроен на возврат количества совпадений во всех случаях.

  • CursorResult.rowcount полезен только в сочетании с оператором UPDATE или DELETE. Вопреки тому, что говорит Python DBAPI, он не возвращает количество доступных строк из результатов оператора SELECT, поскольку DBAPI не может поддерживать эту функциональность, когда строки не буферизованы.

  • CursorResult.rowcount может быть реализован не во всех диалектах. В частности, большинство DBAPI не поддерживают агрегированный результат подсчета строк из вызова executemany. Методы CursorResult.supports_sane_rowcount() и CursorResult.supports_sane_multi_rowcount() будут сообщать диалекту, если известно, что каждое использование поддерживается.

  • Высказывания, использующие RETURNING, могут не возвращать правильное количество строк.

method sqlalchemy.engine.CursorResult.scalar() Any

наследуется от Result.scalar() метода Result

Получить первый столбец первой строки и закрыть набор результатов.

Возвращает None, если нет строк для выборки.

Проверка, остались ли дополнительные строки, не производится.

После вызова этого метода объект будет полностью закрыт, например, будет вызван метод CursorResult.close().

Результат:

скалярное значение Python, или None, если не осталось строк.

method sqlalchemy.engine.CursorResult.scalar_one() Any

наследуется от Result.scalar_one() метода Result

Возвращает ровно один скалярный результат или выдает исключение.

Это эквивалентно вызову Result.scalars(), а затем Result.one().

См.также

Result.one()

Result.scalars()

method sqlalchemy.engine.CursorResult.scalar_one_or_none() Optional[Any]

наследуется от Result.scalar_one_or_none() метода Result

Возвращает ровно один скалярный результат или None.

Это эквивалентно вызову Result.scalars(), а затем Result.one_or_none().

method sqlalchemy.engine.CursorResult.scalars(index: _KeyIndexType = 0) ScalarResult[Any]

наследуется от Result.scalars() метода Result

Возвращает объект фильтрации ScalarResult, который будет возвращать отдельные элементы, а не объекты Row.

Например:

>>> result = conn.execute(text("select int_id from table"))
>>> result.scalars().all()
[1, 2, 3]

Когда результаты извлекаются из объекта фильтрации ScalarResult, в качестве значения столбца возвращается один столбец-строка, который был бы возвращен объектом Result.

Добавлено в версии 1.4.

Параметры:

index – целое число или ключ строки, указывающий столбец, который должен быть извлечен из каждой строки, по умолчанию 0, указывающий на первый столбец.

Результат:

новый объект фильтрации ScalarResult, ссылающийся на данный объект Result.

method sqlalchemy.engine.CursorResult.splice_horizontally(other)

Возвращает новый CursorResult, который «горизонтально сращивает» строки этого CursorResult со строками другого CursorResult.

Совет

Этот метод предназначен для ORM SQLAlchemy и не предназначен для общего использования.

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

Предполагаемый сценарий использования здесь заключается в том, чтобы несколько операторов INSERT…RETURNING (которые определенно должны быть отсортированы) в разных таблицах могли дать один результат, который выглядит как JOIN этих двух таблиц.

Например:

r1 = connection.execute(
    users.insert().returning(
        users.c.user_name,
        users.c.user_id,
        sort_by_parameter_order=True
    ),
    user_values
)

r2 = connection.execute(
    addresses.insert().returning(
        addresses.c.address_id,
        addresses.c.address,
        addresses.c.user_id,
        sort_by_parameter_order=True
    ),
    address_values
)

rows = r1.splice_horizontally(r2).all()
assert (
    rows ==
    [
        ("john", 1, 1, "foo@bar.com", 1),
        ("jack", 2, 2, "bar@bat.com", 2),
    ]
)

Добавлено в версии 2.0.

method sqlalchemy.engine.CursorResult.splice_vertically(other)

Возвращает новый CursorResult, который «вертикально сращивает», т.е. «расширяет», строки данного CursorResult с рядами другого CursorResult.

Совет

Этот метод предназначен для ORM SQLAlchemy и не предназначен для общего использования.

«вертикально сращивает» означает, что строки данного результата добавляются к строкам результата данного курсора. Входящий CursorResult должен иметь строки, которые представляют собой идентичный список столбцов в идентичном порядке, как и в данном CursorResult.

Добавлено в версии 2.0.

method sqlalchemy.engine.CursorResult.supports_sane_multi_rowcount()

Верните supports_sane_multi_rowcount из диалекта.

Смотрите CursorResult.rowcount для справки.

method sqlalchemy.engine.CursorResult.supports_sane_rowcount()

Верните supports_sane_rowcount из диалекта.

Смотрите CursorResult.rowcount для справки.

attribute sqlalchemy.engine.CursorResult.t

наследуется от Result.t атрибута Result

Применить фильтр типизации «типизированный кортеж» к возвращаемым строкам.

Атрибут Result.t является синонимом вызова метода Result.tuples().

Добавлено в версии 2.0.

method sqlalchemy.engine.CursorResult.tuples() TupleResult[_TP]

наследуется от Result.tuples() метода Result

Применить фильтр типизации «типизированный кортеж» к возвращаемым строкам.

Этот метод возвращает тот же объект Result во время выполнения, однако аннотируется как возвращающий объект TupleResult, что укажет инструментам типизации PEP 484, что возвращаются простые типизированные экземпляры Tuple, а не строки. Это позволяет распаковывать кортежи и __getitem__ получать доступ к Row объектам с типизацией, для тех случаев, когда вызываемый оператор содержит информацию о типизации.

Добавлено в версии 2.0.

Результат:

тип TupleResult во время набора текста.

См.также

Result.t - более короткий синоним

Row.t - Row версия

method sqlalchemy.engine.CursorResult.unique(strategy: Optional[Callable[[Any], Any]] = None) Self

наследуется от Result.unique() метода Result

Применить уникальную фильтрацию к объектам, возвращаемым этим Result.

Когда этот фильтр применяется без аргументов, возвращаемые строки или объекты фильтруются таким образом, что каждая строка возвращается уникальной. Алгоритм, используемый для определения уникальности, по умолчанию представляет собой хэширование Python идентификатора всего кортежа. В некоторых случаях может использоваться специализированная схема хэширования для каждого объекта, например, при использовании ORM применяется схема, которая работает против идентичности первичного ключа возвращаемых объектов.

Уникальный фильтр применяется после всех других фильтров, что означает, что если возвращаемые столбцы были уточнены с помощью таких методов, как Result.columns() или Result.scalars(), уникальность применяется только к возвращаемому столбцу или столбцам. Это происходит независимо от порядка, в котором эти методы были вызваны на объекте Result.

Уникальный фильтр также изменяет вычисления, используемые для таких методов, как Result.fetchmany() и Result.partitions(). При использовании Result.unique() эти методы будут продолжать выдавать запрошенное количество строк или объектов после применения уникализации. Однако это обязательно влияет на поведение буферизации базового курсора или источника данных, так что может потребоваться несколько вызовов cursor.fetchmany() для накопления достаточного количества объектов, чтобы обеспечить уникальную коллекцию запрашиваемого размера.

Параметры:

strategy – вызываемый объект, который будет применяться к строкам или объектам, по которым выполняется итерация, и который должен возвращать объект, представляющий уникальное значение строки. Для хранения этих уникальностей используется Python set(). Если он не передан, используется стратегия уникальности по умолчанию, которая, возможно, была собрана источником этого объекта Result.

method sqlalchemy.engine.CursorResult.yield_per(num: int) Self

Настройте стратегию выборки строк на выборку num строк за один раз.

Это влияет на базовое поведение результата при итерации по объекту результата или при использовании таких методов, как Result.fetchone(), которые возвращают по одной строке за раз. Данные из базового курсора или другого источника данных будут буферизироваться в памяти до этого количества строк, а затем буферизированная коллекция будет выдаваться по одной строке за раз или столько строк, сколько запрошено. Каждый раз, когда буфер очищается, он обновляется до этого количества строк или столько, сколько строк осталось, если осталось меньше.

Метод Result.yield_per() обычно используется в сочетании с опцией выполнения Connection.execution_options.stream_results, которая позволит используемому диалекту базы данных использовать курсор на стороне сервера, если DBAPI поддерживает определенный режим «курсора на стороне сервера» отдельно от режима работы по умолчанию.

Совет

Рассмотрите возможность использования опции выполнения Connection.execution_options.yield_per, которая одновременно установит Connection.execution_options.stream_results для обеспечения использования курсоров на стороне сервера, а также автоматически вызовет метод Result.yield_per(), чтобы сразу установить фиксированный размер буфера строк.

Опция выполнения Connection.execution_options.yield_per доступна для операций ORM, а использование , ориентированное на Session, описано в Получение больших наборов результатов с доходностью за. Версия только для Core, работающая с Connection, является новой в SQLAlchemy 1.4.40.

Добавлено в версии 1.4.

Параметры:

num – количество строк для выборки при каждом пополнении буфера. Если задано значение меньше 1, выполняется выборка всех строк для следующего буфера.

class sqlalchemy.engine.FilterResult

Обертка для Result, которая возвращает объекты, отличные от Row, такие как словари или скалярные объекты.

FilterResult является общей базой для дополнительных API результатов, включая MappingResult, ScalarResult и AsyncResult.

Классная подпись

класс sqlalchemy.engine.FilterResult (sqlalchemy.engine.ResultInternal)

method sqlalchemy.engine.FilterResult.close() None

Закройте это FilterResult.

Добавлено в версии 1.4.43.

attribute sqlalchemy.engine.FilterResult.closed

Возвращает True, если базовый отчет Result закрыт

Добавлено в версии 1.4.43.

method sqlalchemy.engine.FilterResult.yield_per(num: int) Self

Настройте стратегию выборки строк на выборку num строк за один раз.

Метод FilterResult.yield_per() является сквозным для метода Result.yield_per(). Заметки по использованию метода см. в документации к нему.

Добавлено в версии 1.4.40: - added FilterResult.yield_per() so that the method is available on all result set implementations

class sqlalchemy.engine.FrozenResult

Представляет объект Result в «замороженном» состоянии, пригодном для кэширования.

Объект FrozenResult возвращается из метода Result.freeze() любого объекта Result.

Новый объект iterable Result генерируется из фиксированного набора данных каждый раз, когда FrozenResult вызывается как callable:

result = connection.execute(query)

frozen = result.freeze()

unfrozen_result_one = frozen()

for row in unfrozen_result_one:
    print(row)

unfrozen_result_two = frozen()
rows = unfrozen_result_two.all()

# ... etc

Добавлено в версии 1.4.

См.также

Повторное выполнение заявлений - пример использования в ORM для реализации кэша наборов результатов.

merge_frozen_result() - функция ORM для объединения замороженного результата обратно в Session.

Классная подпись

класс sqlalchemy.engine.FrozenResult (typing.Generic)

class sqlalchemy.engine.IteratorResult

Result, который получает данные из итератора Python объектов Row или аналогичных строкоподобных данных.

Добавлено в версии 1.4.

Members

closed

Классная подпись

класс sqlalchemy.engine.IteratorResult (sqlalchemy.engine.Result)

attribute sqlalchemy.engine.IteratorResult.closed

Возвращает True, если данный IteratorResult был закрыт

Добавлено в версии 1.4.43.

class sqlalchemy.engine.MergedResult

Объект Result, который объединяется из любого количества объектов Result.

Возвращается методом Result.merge().

Добавлено в версии 1.4.

Классная подпись

класс sqlalchemy.engine.MergedResult (sqlalchemy.engine.IteratorResult)

class sqlalchemy.engine.Result

Представляют собой набор результатов базы данных.

Добавлено в версии 1.4: Объект Result предоставляет полностью обновленную модель использования и фасад вызовов для SQLAlchemy Core и SQLAlchemy ORM. В Core он составляет основу объекта CursorResult, который заменяет предыдущий интерфейс ResultProxy. При использовании ORM обычно используется объект более высокого уровня под названием ChunkedIteratorResult.

Примечание

В SQLAlchemy 1.4 и выше этот объект используется для результатов ORM, возвращаемых Session.execute(), которые могут давать экземпляры сопоставленных с ORM объектов по отдельности или в составе кортежей. Обратите внимание, что объект Result не дедуплицирует экземпляры или строки автоматически, как в случае с устаревшим объектом Query. Для дедупликации экземпляров или строк в Python используйте метод-модификатор Result.unique().

Классная подпись

класс sqlalchemy.engine.Result (sqlalchemy.engine._WithKeys, sqlalchemy.engine.ResultInternal)

method sqlalchemy.engine.Result.all() Sequence[Row[_TP]]

Возвращает все строки в списке.

Закрывает набор результатов после вызова. Последующие вызовы будут возвращать пустой список.

Добавлено в версии 1.4.

Результат:

список объектов Row.

method sqlalchemy.engine.Result.close() None

закрыть эту Result.

Поведение этого метода зависит от реализации и не реализовано по умолчанию. Метод обычно должен завершать ресурсы, используемые объектом result, а также приводить к тому, что любая последующая итерация или выборка строк вызывает ошибку ResourceClosedError.

Добавлено в версии 1.4.27: - .close() was previously not generally available for all Result classes, instead only being available on the CursorResult returned for Core statement executions. As most other result objects, namely the ones used by the ORM, are proxying a CursorResult in any case, this allows the underlying cursor result to be closed from the outside facade for the case when the ORM query is using the yield_per execution option where it does not immediately exhaust and autoclose the database cursor.

attribute sqlalchemy.engine.Result.closed

возвращается True, если это Result сообщает .closed

Добавлено в версии 1.4.43.

method sqlalchemy.engine.Result.columns(*col_expressions: _KeyIndexType) Self

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

Этот метод можно использовать для ограничения возвращаемых столбцов, а также для их переупорядочивания. Заданный список выражений обычно представляет собой ряд целых чисел или строковых имен ключей. Это также могут быть соответствующие объекты ColumnElement, которые соответствуют заданной конструкции оператора.

Изменено в версии 2.0: Из-за ошибки в версии 1.4 метод Result.columns() имел некорректное поведение, когда вызов метода с одним индексом приводил к тому, что объект Result выдавал скалярные значения, а не объекты Row. В версии 2.0 это поведение было исправлено, и теперь вызов метода Result.columns() с одним индексом приводит к созданию объекта Result, который продолжает выдавать объекты Row, включающие только один столбец.

Например:

statement = select(table.c.x, table.c.y, table.c.z)
result = connection.execute(statement)

for z, y in result.columns('z', 'y'):
    # ...

Пример использования объектов столбцов из самого оператора:

for z, y in result.columns(
        statement.selected_columns.c.z,
        statement.selected_columns.c.y
):
    # ...

Добавлено в версии 1.4.

Параметры:

*col_expressions – указывает на возвращаемые столбцы. Элементами могут быть целочисленные индексы строк, строковые имена столбцов или соответствующие объекты ColumnElement, соответствующие конструкции select.

Результат:

этот объект Result с заданными модификациями.

method sqlalchemy.engine.Result.fetchall() Sequence[Row[_TP]]

Синоним метода Result.all().

method sqlalchemy.engine.Result.fetchmany(size: Optional[int] = None) Sequence[Row[_TP]]

Получение большого количества строк.

Когда все строки исчерпаны, возвращает пустой список.

Этот метод предусмотрен для обратной совместимости с SQLAlchemy 1.x.x.

Чтобы получать строки группами, используйте метод Result.partitions().

Результат:

список объектов Row.

См.также

Result.partitions()

method sqlalchemy.engine.Result.fetchone() Optional[Row[_TP]]

Получение одного ряда.

Если все строки исчерпаны, возвращается None.

Этот метод предусмотрен для обратной совместимости с SQLAlchemy 1.x.x.

Чтобы получить только первую строку результата, используйте метод Result.first(). Для итерации по всем строкам используйте непосредственно объект Result.

Результат:

объект Row, если фильтры не применяются, или None, если не осталось ни одной строки.

method sqlalchemy.engine.Result.first() Optional[Row[_TP]]

Получить первую строку или None, если строка отсутствует.

Закрывает набор результатов и отбрасывает оставшиеся строки.

Примечание

По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar() или комбинируйте Result.scalars() и Result.first().

Кроме того, в отличие от поведения унаследованного метода ORM Query.first(), не применяется ограничение к SQL-запросу, который был вызван для создания этого Result; для драйвера DBAPI, который буферизирует результаты в памяти перед выдачей строк, все строки будут отправлены в процесс Python, и все, кроме первой строки, будут отброшены.

Результат:

объект Row, или None, если не осталось строк.

См.также

Result.scalar()

Result.one()

method sqlalchemy.engine.Result.freeze() FrozenResult[_TP]

Возвращает вызываемый объект, который при вызове будет создавать копии этого Result.

Возвращаемый вызываемый объект является экземпляром FrozenResult.

Используется для кэширования набора результатов. Метод должен быть вызван на результате, когда он не был поглощен, и вызов метода полностью поглотит результат. Когда FrozenResult извлекается из кэша, его можно вызывать любое количество раз, и каждый раз он будет создавать новый объект Result по сохраненному набору строк.

См.также

Повторное выполнение заявлений - пример использования в ORM для реализации кэша наборов результатов.

method sqlalchemy.engine.Result.keys() RMKeyView

наследуется от sqlalchemy.engine._WithKeys.keys метода sqlalchemy.engine._WithKeys

Возвращает итерируемое представление, которое дает ключи строк, которые были бы представлены каждым Row.

Ключи могут представлять собой метки столбцов, возвращаемых оператором core, или имена классов orm, возвращаемых выполнением orm.

Представление также можно проверить на содержание ключей с помощью оператора Python in, который будет проверять как строковые ключи, представленные в представлении, так и альтернативные ключи, такие как объекты столбцов.

Изменено в версии 1.4: возвращается объект представления ключа, а не обычный список.

method sqlalchemy.engine.Result.mappings() MappingResult

Применить фильтр сопоставлений к возвращаемым строкам, возвращая экземпляр MappingResult.

Когда применяется этот фильтр, при выборке строк будут возвращаться объекты RowMapping, а не Row.

Добавлено в версии 1.4.

Результат:

новый объект фильтрации MappingResult, ссылающийся на данный объект Result.

method sqlalchemy.engine.Result.merge(*others: Result[Any]) MergedResult[_TP]

Объедините этот Result с другими совместимыми объектами результата.

Возвращаемый объект является экземпляром MergedResult, который будет состоять из итераторов от заданных объектов результата.

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

method sqlalchemy.engine.Result.one() Row[_TP]

Вернуть ровно одну строку или выдать исключение.

Вызывает NoResultFound, если результат не возвращает ни одной строки, или MultipleResultsFound, если будет возвращено несколько строк.

Примечание

По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar_one() или комбинируйте Result.scalars() и Result.one().

Добавлено в версии 1.4.

Результат:

Первый Row.

поднимает:

MultipleResultsFound, NoResultFound

method sqlalchemy.engine.Result.one_or_none() Optional[Row[_TP]]

Возвращает не более одного результата или выдает исключение.

Возвращает None, если результат не содержит строк. Возвращает MultipleResultsFound, если возвращается несколько строк.

Добавлено в версии 1.4.

Результат:

Первая строка Row или None, если строка отсутствует.

поднимает:

MultipleResultsFound

См.также

Result.first()

Result.one()

method sqlalchemy.engine.Result.partitions(size: Optional[int] = None) Iterator[Sequence[Row[_TP]]]

Итерация по подспискам строк заданного размера.

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

Объект result автоматически закрывается, когда итератор полностью израсходован.

Обратите внимание, что драйвер бэкенда обычно буферизирует весь результат заранее, если только не используется опция выполнения Connection.execution_options.stream_results, указывающая, что драйвер не должен предварительно буферизировать результаты, если это возможно. Не все драйверы поддерживают эту опцию, и для тех, кто ее не поддерживает, опция игнорируется.

При использовании ORM метод Result.partitions() обычно более эффективен с точки зрения памяти, когда он сочетается с использованием yield_per execution option, который инструктирует как драйвер DBAPI использовать курсоры на стороне сервера, если они доступны, так и инструктирует внутренние механизмы загрузки ORM создавать только определенное количество объектов ORM из результата за один раз, прежде чем выдавать их.

Добавлено в версии 1.4.

Параметры:

size – указывает максимальное количество строк, которое должно присутствовать в каждом выдаваемом списке. Если None, используется значение, установленное методом Result.yield_per(), если он был вызван, или опция выполнения Connection.execution_options.yield_per, которая эквивалентна в этом отношении. Если yield_per не задан, то используется значение по умолчанию Result.fetchmany(), которое может быть специфичным для бэкенда и не вполне определенным.

Результат:

итератор списков

method sqlalchemy.engine.Result.scalar() Any

Получить первый столбец первой строки и закрыть набор результатов.

Возвращает None, если нет строк для выборки.

Проверка, остались ли дополнительные строки, не производится.

После вызова этого метода объект будет полностью закрыт, например, будет вызван метод CursorResult.close().

Результат:

скалярное значение Python, или None, если не осталось строк.

method sqlalchemy.engine.Result.scalar_one() Any

Возвращает ровно один скалярный результат или выдает исключение.

Это эквивалентно вызову Result.scalars(), а затем Result.one().

См.также

Result.one()

Result.scalars()

method sqlalchemy.engine.Result.scalar_one_or_none() Optional[Any]

Возвращает ровно один скалярный результат или None.

Это эквивалентно вызову Result.scalars(), а затем Result.one_or_none().

method sqlalchemy.engine.Result.scalars(index: _KeyIndexType = 0) ScalarResult[Any]

Возвращает объект фильтрации ScalarResult, который будет возвращать отдельные элементы, а не объекты Row.

Например:

>>> result = conn.execute(text("select int_id from table"))
>>> result.scalars().all()
[1, 2, 3]

Когда результаты извлекаются из объекта фильтрации ScalarResult, в качестве значения столбца возвращается один столбец-строка, который был бы возвращен объектом Result.

Добавлено в версии 1.4.

Параметры:

index – целое число или ключ строки, указывающий столбец, который должен быть извлечен из каждой строки, по умолчанию 0, указывающий на первый столбец.

Результат:

новый объект фильтрации ScalarResult, ссылающийся на данный объект Result.

attribute sqlalchemy.engine.Result.t

Применить фильтр типизации «типизированный кортеж» к возвращаемым строкам.

Атрибут Result.t является синонимом вызова метода Result.tuples().

Добавлено в версии 2.0.

method sqlalchemy.engine.Result.tuples() TupleResult[_TP]

Применить фильтр типизации «типизированный кортеж» к возвращаемым строкам.

Этот метод возвращает тот же объект Result во время выполнения, однако аннотируется как возвращающий объект TupleResult, что укажет инструментам типизации PEP 484, что возвращаются простые типизированные экземпляры Tuple, а не строки. Это позволяет распаковывать кортежи и __getitem__ получать доступ к Row объектам с типизацией, для тех случаев, когда вызываемый оператор содержит информацию о типизации.

Добавлено в версии 2.0.

Результат:

тип TupleResult во время набора текста.

См.также

Result.t - более короткий синоним

Row.t - Row версия

method sqlalchemy.engine.Result.unique(strategy: Optional[Callable[[Any], Any]] = None) Self

Применить уникальную фильтрацию к объектам, возвращаемым этим Result.

Когда этот фильтр применяется без аргументов, возвращаемые строки или объекты фильтруются таким образом, что каждая строка возвращается уникальной. Алгоритм, используемый для определения уникальности, по умолчанию представляет собой хэширование Python идентификатора всего кортежа. В некоторых случаях может использоваться специализированная схема хэширования для каждого объекта, например, при использовании ORM применяется схема, которая работает против идентичности первичного ключа возвращаемых объектов.

Уникальный фильтр применяется после всех других фильтров, что означает, что если возвращаемые столбцы были уточнены с помощью таких методов, как Result.columns() или Result.scalars(), уникальность применяется только к возвращаемому столбцу или столбцам. Это происходит независимо от порядка, в котором эти методы были вызваны на объекте Result.

Уникальный фильтр также изменяет вычисления, используемые для таких методов, как Result.fetchmany() и Result.partitions(). При использовании Result.unique() эти методы будут продолжать выдавать запрошенное количество строк или объектов после применения уникализации. Однако это обязательно влияет на поведение буферизации базового курсора или источника данных, так что может потребоваться несколько вызовов cursor.fetchmany() для накопления достаточного количества объектов, чтобы обеспечить уникальную коллекцию запрашиваемого размера.

Параметры:

strategy – вызываемый объект, который будет применяться к строкам или объектам, по которым выполняется итерация, и который должен возвращать объект, представляющий уникальное значение строки. Для хранения этих уникальностей используется Python set(). Если он не передан, используется стратегия уникальности по умолчанию, которая, возможно, была собрана источником этого объекта Result.

method sqlalchemy.engine.Result.yield_per(num: int) Self

Настройте стратегию выборки строк на выборку num строк за один раз.

Это влияет на базовое поведение результата при итерации по объекту результата или при использовании таких методов, как Result.fetchone(), которые возвращают по одной строке за раз. Данные из базового курсора или другого источника данных будут буферизироваться в памяти до этого количества строк, а затем буферизированная коллекция будет выдаваться по одной строке за раз или столько строк, сколько запрошено. Каждый раз, когда буфер очищается, он обновляется до этого количества строк или столько, сколько строк осталось, если осталось меньше.

Метод Result.yield_per() обычно используется в сочетании с опцией выполнения Connection.execution_options.stream_results, которая позволит используемому диалекту базы данных использовать курсор на стороне сервера, если DBAPI поддерживает определенный режим «курсора на стороне сервера» отдельно от режима работы по умолчанию.

Совет

Рассмотрите возможность использования опции выполнения Connection.execution_options.yield_per, которая одновременно установит Connection.execution_options.stream_results для обеспечения использования курсоров на стороне сервера, а также автоматически вызовет метод Result.yield_per(), чтобы сразу установить фиксированный размер буфера строк.

Опция выполнения Connection.execution_options.yield_per доступна для операций ORM, а использование , ориентированное на Session, описано в Получение больших наборов результатов с доходностью за. Версия только для Core, работающая с Connection, является новой в SQLAlchemy 1.4.40.

Добавлено в версии 1.4.

Параметры:

num – количество строк для выборки при каждом пополнении буфера. Если задано значение меньше 1, выполняется выборка всех строк для следующего буфера.

class sqlalchemy.engine.ScalarResult

Обертка для Result, которая возвращает скалярные значения, а не значения Row.

Объект ScalarResult приобретается путем вызова метода Result.scalars().

Особое ограничение ScalarResult заключается в том, что у него нет метода fetchone(); поскольку семантика fetchone() заключается в том, что значение None указывает на отсутствие результатов, это несовместимо с ScalarResult, поскольку нет способа отличить None как значение строки от None как индикатора. Используйте next(result) для получения значений по отдельности.

Классная подпись

класс sqlalchemy.engine.ScalarResult (sqlalchemy.engine.FilterResult)

method sqlalchemy.engine.ScalarResult.all() Sequence[_R]

Возвращает все скалярные значения в списке.

Эквивалентно Result.all() за исключением того, что возвращаются скалярные значения, а не объекты Row.

method sqlalchemy.engine.ScalarResult.close() None

наследуется от FilterResult.close() метода FilterResult

Закройте это FilterResult.

Добавлено в версии 1.4.43.

attribute sqlalchemy.engine.ScalarResult.closed

наследуется от FilterResult.closed атрибута FilterResult

Возвращает True, если базовый отчет Result закрыт

Добавлено в версии 1.4.43.

method sqlalchemy.engine.ScalarResult.fetchall() Sequence[_R]

Синоним метода ScalarResult.all().

method sqlalchemy.engine.ScalarResult.fetchmany(size: Optional[int] = None) Sequence[_R]

Получение большого количества объектов.

Эквивалентно Result.fetchmany() за исключением того, что возвращаются скалярные значения, а не объекты Row.

method sqlalchemy.engine.ScalarResult.first() Optional[_R]

Получение первого объекта или None, если объект отсутствует.

Эквивалентно Result.first() за исключением того, что возвращаются скалярные значения, а не объекты Row.

method sqlalchemy.engine.ScalarResult.one() _R

Возвращает ровно один объект или вызывает исключение.

Эквивалентно Result.one() за исключением того, что возвращаются скалярные значения, а не объекты Row.

method sqlalchemy.engine.ScalarResult.one_or_none() Optional[_R]

Возвращает не более одного объекта или вызывает исключение.

Эквивалентно Result.one_or_none() за исключением того, что возвращаются скалярные значения, а не объекты Row.

method sqlalchemy.engine.ScalarResult.partitions(size: Optional[int] = None) Iterator[Sequence[_R]]

Итерация по подспискам элементов заданного размера.

Эквивалентно Result.partitions() за исключением того, что возвращаются скалярные значения, а не объекты Row.

method sqlalchemy.engine.ScalarResult.unique(strategy: Optional[Callable[[Any], Any]] = None) Self

Применить уникальную фильтрацию к объектам, возвращаемым этим ScalarResult.

Подробности использования см. в разделе Result.unique().

method sqlalchemy.engine.ScalarResult.yield_per(num: int) Self

наследуется от FilterResult.yield_per() метода FilterResult

Настройте стратегию выборки строк на выборку num строк за один раз.

Метод FilterResult.yield_per() является сквозным для метода Result.yield_per(). Заметки по использованию метода см. в документации к нему.

Добавлено в версии 1.4.40: - added FilterResult.yield_per() so that the method is available on all result set implementations

class sqlalchemy.engine.MappingResult

Обертка для Result, которая возвращает словарные значения, а не значения Row.

Объект MappingResult приобретается путем вызова метода Result.mappings().

Классная подпись

класс sqlalchemy.engine.MappingResult (sqlalchemy.engine._WithKeys, sqlalchemy.engine.FilterResult)

method sqlalchemy.engine.MappingResult.all() Sequence[RowMapping]

Возвращает все скалярные значения в списке.

Эквивалентно Result.all() за исключением того, что возвращаются значения RowMapping, а не объекты Row.

method sqlalchemy.engine.MappingResult.close() None

наследуется от FilterResult.close() метода FilterResult

Закройте это FilterResult.

Добавлено в версии 1.4.43.

attribute sqlalchemy.engine.MappingResult.closed

наследуется от FilterResult.closed атрибута FilterResult

Возвращает True, если базовый отчет Result закрыт

Добавлено в версии 1.4.43.

method sqlalchemy.engine.MappingResult.columns(*col_expressions: _KeyIndexType) Self

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

method sqlalchemy.engine.MappingResult.fetchall() Sequence[RowMapping]

Синоним метода MappingResult.all().

method sqlalchemy.engine.MappingResult.fetchmany(size: Optional[int] = None) Sequence[RowMapping]

Получение большого количества объектов.

Эквивалентно Result.fetchmany() за исключением того, что возвращаются значения RowMapping, а не объекты Row.

method sqlalchemy.engine.MappingResult.fetchone() Optional[RowMapping]

Получение одного объекта.

Эквивалентно Result.fetchone() за исключением того, что возвращаются значения RowMapping, а не объекты Row.

method sqlalchemy.engine.MappingResult.first() Optional[RowMapping]

Получение первого объекта или None, если объект отсутствует.

Эквивалентно Result.first() за исключением того, что возвращаются значения RowMapping, а не объекты Row.

method sqlalchemy.engine.MappingResult.keys() RMKeyView

наследуется от sqlalchemy.engine._WithKeys.keys метода sqlalchemy.engine._WithKeys

Возвращает итерируемое представление, которое дает ключи строк, которые были бы представлены каждым Row.

Ключи могут представлять собой метки столбцов, возвращаемых оператором core, или имена классов orm, возвращаемых выполнением orm.

Представление также можно проверить на содержание ключей с помощью оператора Python in, который будет проверять как строковые ключи, представленные в представлении, так и альтернативные ключи, такие как объекты столбцов.

Изменено в версии 1.4: возвращается объект представления ключа, а не обычный список.

method sqlalchemy.engine.MappingResult.one() RowMapping

Возвращает ровно один объект или вызывает исключение.

Эквивалентно Result.one() за исключением того, что возвращаются значения RowMapping, а не объекты Row.

method sqlalchemy.engine.MappingResult.one_or_none() Optional[RowMapping]

Возвращает не более одного объекта или вызывает исключение.

Эквивалентно Result.one_or_none() за исключением того, что возвращаются значения RowMapping, а не объекты Row.

method sqlalchemy.engine.MappingResult.partitions(size: Optional[int] = None) Iterator[Sequence[RowMapping]]

Итерация по подспискам элементов заданного размера.

Эквивалентно Result.partitions() за исключением того, что возвращаются значения RowMapping, а не объекты Row.

method sqlalchemy.engine.MappingResult.unique(strategy: Optional[Callable[[Any], Any]] = None) Self

Применить уникальную фильтрацию к объектам, возвращаемым этим MappingResult.

Подробности использования см. в разделе Result.unique().

method sqlalchemy.engine.MappingResult.yield_per(num: int) Self

наследуется от FilterResult.yield_per() метода FilterResult

Настройте стратегию выборки строк на выборку num строк за один раз.

Метод FilterResult.yield_per() является сквозным для метода Result.yield_per(). Заметки по использованию метода см. в документации к нему.

Добавлено в версии 1.4.40: - added FilterResult.yield_per() so that the method is available on all result set implementations

class sqlalchemy.engine.Row

Представляют собой одну строку результата.

Объект Row представляет строку результата базы данных. В SQLAlchemy серии 1.x он обычно ассоциируется с объектом CursorResult, однако начиная с SQLAlchemy 1.4 он также используется ORM для кортежей результатов.

Объект Row стремится действовать как можно больше похоже на именованный кортеж Python. Для поведения отображения (т.е. словаря) в строке, такого как проверка на содержание ключей, обратитесь к атрибуту Row._mapping.

См.также

Использование операторов SELECT - включает примеры выбора строк из операторов SELECT.

Изменено в версии 1.4: Переименовали RowProxy в Row. Row больше не является «прокси» объектом, поскольку он содержит конечную форму данных внутри себя, и теперь действует в основном как именованный кортеж. Функциональность, подобная отображению, перенесена в атрибут Row._mapping. Смотрите RowProxy больше не является «прокси»; теперь он называется Row и ведет себя как расширенный именованный кортеж для справки об этом изменении.

Классная подпись

class sqlalchemy.engine.Row (sqlalchemy.engine._py_row.BaseRow, collections.abc.Sequence, typing.Generic)

method sqlalchemy.engine.Row._asdict() Dict[str, Any]

Возвращает новый dict, который сопоставляет имена полей с соответствующими значениями.

Этот метод аналогичен методу Python named tuple ._asdict() и работает путем применения конструктора dict() к атрибуту Row._mapping.

Добавлено в версии 1.4.

См.также

Row._mapping

attribute sqlalchemy.engine.Row._fields

Возвращает кортеж строковых ключей, представленных данным Row.

Ключи могут представлять собой метки столбцов, возвращаемых оператором core, или имена классов orm, возвращаемых выполнением orm.

Этот атрибут аналогичен атрибуту Python named tuple ._fields.

Добавлено в версии 1.4.

См.также

Row._mapping

attribute sqlalchemy.engine.Row._mapping

Возвращает RowMapping для данного Row.

Этот объект предоставляет последовательный интерфейс отображения (т.е. словарь) Python для данных, содержащихся в строке. Сам по себе Row ведет себя как именованный кортеж.

См.также

Row._fields

Добавлено в версии 1.4.

attribute sqlalchemy.engine.Row.count
attribute sqlalchemy.engine.Row.index
attribute sqlalchemy.engine.Row.t

синоним для Row.tuple

Добавлено в версии 2.0.

См.также

Result.t()

method sqlalchemy.engine.Row.tuple() _TP

Возвращает „кортеж“ формы данного Row.

Во время выполнения этот метод возвращает «self»; объект Row уже является именованным кортежем. Однако на уровне типизации, если этот Row типизирован, возвращаемый тип «кортеж» будет типом данных PEP 484 Tuple, который содержит информацию о типизации отдельных элементов, поддерживая типизированную распаковку и доступ к атрибутам.

Добавлено в версии 2.0.

См.также

Result.tuples()

class sqlalchemy.engine.RowMapping

Mapping, который сопоставляет имена столбцов и объектов со значениями Row.

RowMapping доступен из Row через атрибут Row._mapping, а также из интерфейса iterable, предоставляемого объектом MappingResult, возвращаемым методом Result.mappings().

RowMapping предоставляет доступ к содержимому строки через Python mapping (т.е. словарь). Это включает поддержку проверки содержания определенных ключей (строковых имен столбцов или объектов), а также итерации ключей, значений и элементов:

for row in result:
    if 'a' in row._mapping:
        print("Column 'a': %s" % row._mapping['a'])

    print("Column b: %s" % row._mapping[table.c.b])

Добавлено в версии 1.4: Объект RowMapping заменяет доступ, подобный отображению, ранее предоставляемый строкой результата базы данных, которая теперь стремится вести себя в основном как именованный кортеж.

Members

items(), keys(), values()

Классная подпись

class sqlalchemy.engine.RowMapping (sqlalchemy.engine._py_row.BaseRow, collections.abc.Mapping, typing.Generic)

method sqlalchemy.engine.RowMapping.items() ROMappingItemsView

Возвращает представление кортежей ключ/значение для элементов в базовом Row.

method sqlalchemy.engine.RowMapping.keys() RMKeyView

Возвращает представление «ключей» для строковых имен столбцов, представленных базовым Row.

method sqlalchemy.engine.RowMapping.values() ROMappingKeysValuesView

:class:`.Row`R

class sqlalchemy.engine.TupleResult

Result, который типизирован как возвращающий обычные кортежи Python вместо строк.

Row Result S

Классная подпись

класс sqlalchemy.engine.TupleResult (sqlalchemy.engine.FilterResult, sqlalchemy.util.langhelpers.TypingOnly)

Вернуться на верх