В этом разделе описывается прямое использование объектов Engine, Connection и связанных с ними объектов. Важно отметить, что при использовании SQLAlchemy ORM к этим объектам обычно не обращаются; вместо этого в качестве интерфейса к базе данных используется объект Session. Однако для приложений, построенных на прямом использовании текстовых операторов SQL и/или конструкций выражений SQL без участия служб управления более высокого уровня ORM, Engine и Connection являются королем (и королевой?) - читайте далее.
Обычно create_engine() используется один раз для каждого конкретного URL базы данных, хранящегося глобально в течение всего времени жизни одного прикладного процесса. Один Engine управляет множеством отдельных соединений DBAPI от имени процесса и предназначен для одновременного обращения к нему. Engine является не синонимом функции DBAPI connect(), которая представляет только один ресурс соединения - Engine наиболее эффективен, когда создается только один раз на уровне модуля приложения, а не на каждый объект или вызов каждой функции.
Самая основная функция Engine заключается в предоставлении доступа к Connection, который затем может вызывать SQL-запросы. Вызов текстового оператора к базе данных выглядит следующим образом:
fromsqlalchemyimporttextwithengine.connect()asconnection:result=connection.execute(text("select username from users"))forrowinresult: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, и он показан на примере ниже:
withengine.connect()asconnection: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(); каждый раз, когда транзакция завершается и испускается новое утверждение, неявно начинается новая транзакция:
withengine.connect()asconnection:connection.execute("<some statement>")connection.commit()# commits "some statement"# new transaction startsconnection.execute("<some other statement>")connection.rollback()# rolls back "some other statement"# new transaction startsconnection.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»:
withengine.connect()asconnection:withconnection.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():
withengine.begin()asconnection: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 до тех пор, пока блок не завершится:
>>> fromsqlalchemyimportcreate_engine>>> e=create_engine("sqlite://",echo=True)>>> withe.begin()asconn:... 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 COMMITTraceback (most recent call last):...sqlalchemy.exc.InvalidRequestError: Can't operate on closed transaction insidecontext manager. Please complete the context manager before emittingfurther commands.
Стили «commit as you go» и «begin once» могут свободно смешиваться в одном блоке Engine.connect(), при условии, что вызов Connection.begin() не конфликтует с поведением «autobegin». Для этого Connection.begin() следует вызывать только до того, как были выполнены какие-либо SQL-запросы, или непосредственно после предыдущего вызова Connection.commit() или Connection.rollback():
withengine.connect()asconnection:withconnection.begin():# run statements in a "begin once" blockconnection.execute(some_table.insert(),{"x":7,"y":"this is some data"})# transaction is committed# run a new statement outside of a block. The connection# autobeginsconnection.execute(some_other_table.insert(),{"q":8,"p":"this is some more data"})# commit explicitlyconnection.commit()# can use a "begin once" block herewithconnection.begin():# run more statementsconnection.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 для соединения¶
# possible values for Connection.execution_options(isolation_level="<value>")"AUTOCOMMIT""READ COMMITTED""READ UNCOMMITTED""REPEATABLE READ""SERIALIZABLE"
Не каждый DBAPI поддерживает все значения; если для определенного бэкенда используется неподдерживаемое значение, выдается ошибка.
Например, чтобы заставить REPEATABLE READ на определенном соединении, начните транзакцию:
Возвращаемым значением метода Connection.execution_options() является тот же объект Connection, на котором был вызван метод, то есть он изменяет состояние объекта Connection на месте. Это новое поведение, начиная с версии SQLAlchemy 2.0. Это поведение не относится к методу Engine.execution_options(); этот метод по-прежнему возвращает копию Engine и, как описано ниже, может использоваться для создания нескольких объектов Engine с различными вариантами выполнения, которые, тем не менее, используют один и тот же диалект и пул соединений.
Примечание
Параметр Connection.execution_options.isolation_level обязательно не применяется к опциям уровня оператора, таким как Executable.execution_options(), и будет отклонен, если установлен на этом уровне. Это связано с тем, что параметр должен быть установлен на DBAPI-соединении на основе каждой транзакции.
Настройка уровня изоляции или автокоммита DBAPI для движка¶
С приведенной выше настройкой каждое новое соединение DBAPI в момент его создания будет настроено на использование уровня изоляции "REPEATABLEREAD" для всех последующих операций.
Поддержание нескольких уровней изоляции для одного двигателя¶
Уровень изоляции также может быть установлен для каждого двигателя, с потенциально большим уровнем гибкости, используя либо параметр create_engine.execution_options для create_engine(), либо метод Engine.execution_options(), последний из которых создаст копию Engine, которая разделяет диалект и пул соединений оригинального двигателя, но имеет свои собственные настройки уровня изоляции для каждого соединения:
С приведенным выше параметром соединение DBAPI будет настроено на использование уровня изоляции "REPEATABLEREAD" для каждой новой начатой транзакции; но соединение как объединенное будет возвращено к исходному уровню изоляции, который присутствовал при первом возникновении соединения. На уровне create_engine() конечный эффект не отличается от использования параметра create_engine.isolation_level.
Однако приложение, которое часто решает выполнять операции на разных уровнях изоляции, может пожелать создать несколько «под-движков» ведущего Engine, каждый из которых будет настроен на разный уровень изоляции. Одним из таких случаев является приложение, в котором операции разделяются на «транзакционные» и «только для чтения», отдельный Engine, использующий "AUTOCOMMIT", может быть отделен от основного двигателя:
Выше, метод Engine.execution_options() создает неглубокую копию исходного Engine. Оба eng и autocommit_engine используют один и тот же диалект и пул соединений. Однако режим «AUTOCOMMIT» будет установлен на соединениях, когда они будут получены от autocommit_engine.
Настройка уровня изоляции, независимо от того, какая она, безоговорочно возвращается, когда соединение возвращается в пул соединений.
Понимание уровня изоляции автокоммита на уровне DBAPI¶
В предыдущем разделе мы познакомились с понятием параметра Connection.execution_options.isolation_level и тем, как его можно использовать для установки уровней изоляции базы данных, включая «автокоммит» на уровне DBAPI, который рассматривается SQLAlchemy как еще один уровень изоляции транзакций. В этом разделе мы попытаемся прояснить последствия такого подхода.
Если бы мы хотели проверить объект Connection и использовать его в режиме «autocommit», мы бы поступили следующим образом:
Выше показано обычное использование режима «DBAPI autocommit». Нет необходимости использовать такие методы, как Connection.begin() или Connection.commit(), поскольку все утверждения фиксируются в базе данных немедленно. Когда блок завершается, объект Connection возвращает уровень изоляции «autocommit», и соединение DBAPI освобождается в пул соединений, где обычно вызывается метод DBAPI connection.rollback(), но поскольку вышеприведенные утверждения уже были зафиксированы, этот откат не оказывает никакого влияния на состояние базы данных.
Важно отметить, что режим «autocommit» сохраняется даже при вызове метода Connection.begin(); DBAPI не будет передавать в базу данных ни BEGIN, ни COMMIT при вызове Connection.commit(). Такое использование также не является сценарием ошибки, поскольку ожидается, что уровень изоляции «autocommit» может быть применен к коду, который в противном случае был написан с учетом транзакционного контекста; «уровень изоляции», в конце концов, является конфигурационной деталью самой транзакции, как и любой другой уровень изоляции.
В приведенном ниже примере утверждения остаются автокоммитом независимо от блоков транзакций уровня SQLAlchemy:
withengine.connect()asconnection:connection=connection.execution_options(isolation_level="AUTOCOMMIT")# this begin() does not affect the DBAPI connection, isolation stays at AUTOCOMMITwithconnection.begin()astrans: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 уже произошел:
withengine.connect()asconnection: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 begunwithconnection.begin()astrans: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.withengine.connect()asconnection:connection.execution_options(isolation_level="AUTOCOMMIT")# run statement(s) in autocommit modeconnection.execute("<statement>")# "commit" the autobegun "transaction"connection.commit()# switch to default isolation levelconnection.execution_options(isolation_level=connection.default_isolation_level)# use a begin blockwithconnection.begin()astrans:connection.execute("<statement>")
Выше, чтобы вручную изменить уровень изоляции, мы использовали Connection.default_isolation_level для восстановления уровня изоляции по умолчанию (предполагая, что это то, что мы хотим здесь). Однако, вероятно, лучше работать с архитектурой Connection, которая уже обрабатывает сброс уровня изоляции автоматически при регистрации. предпочтительный способ написать вышеописанное - использовать два блока
# use an autocommit blockwithengine.connect().execution_options(isolation_level="AUTOCOMMIT")asconnection:# run statement in autocommit modeconnection.execute("<statement>")# use a regular blockwithengine.begin()asconnection:connection.execute("<statement>")
Подведем итоги:
Уровень изоляции «автокоммит на уровне DBAPI» полностью независим от понятия «начать» и «зафиксировать» объекта Connection.
используйте отдельные проверки Connection для каждого уровня изоляции. Избегайте попыток переключения туда и обратно между «autocommit» при проверке одного соединения; позвольте движку выполнить работу по восстановлению уровней изоляции по умолчанию
Использование курсоров на стороне сервера (они же потоковые результаты)¶
Некоторые бэкенды имеют явную поддержку концепции «курсоров на стороне сервера» и «курсоров на стороне клиента». Курсор на стороне клиента означает, что драйвер базы данных полностью забирает все строки из набора результатов в память перед возвратом после выполнения оператора. Такие драйверы, как PostgreSQL и MySQL/MariaDB, обычно используют курсоры на стороне клиента по умолчанию. Курсор на стороне сервера, напротив, указывает на то, что строки результатов остаются в состоянии ожидания на сервере базы данных, пока строки результатов потребляются клиентом. Например, драйверы для Oracle обычно используют модель «на стороне сервера», а диалект SQLite, хотя и не использует настоящую архитектуру «клиент/сервер», все же использует подход небуферизованной выборки результатов, который оставляет строки результатов вне памяти процесса до того, как они будут потреблены.
Из этой базовой архитектуры следует, что «курсор на стороне сервера» более эффективен при получении очень больших наборов результатов, но в то же время может усложнить процесс взаимодействия клиента и сервера и быть менее эффективным для небольших наборов результатов (обычно менее 10000 строк).
Для тех диалектов, которые имеют условную поддержку буферизованных или небуферизованных результатов, обычно существуют ограничения на использование «небуферизованного» режима, или режима курсора на стороне сервера. При использовании диалекта psycopg2, например, ошибка возникает, если курсор на стороне сервера используется с любым типом оператора DML или DDL. При использовании драйверов MySQL с курсором на стороне сервера, соединение DBAPI находится в более хрупком состоянии и не восстанавливается так же изящно после ошибок, а также не позволяет выполнить откат до тех пор, пока курсор не будет полностью закрыт.
По этой причине диалекты SQLAlchemy всегда по умолчанию используют менее подверженную ошибкам версию курсора, что означает, что для диалектов PostgreSQL и MySQL по умолчанию используется буферизованный курсор «на стороне клиента», где полный набор результатов забирается в память до вызова любых методов выборки из курсора. Такой режим работы подходит в подавляющем большинстве случаев; небуферизированные курсоры, как правило, не полезны, за исключением редких случаев, когда приложение получает очень большое количество строк по частям, где обработка этих строк может быть завершена до получения большего количества строк.
Потоковая передача с фиксированным буфером через yield_per¶
Поскольку отдельные операции выборки строк с полностью небуферизованными курсорами на стороне сервера обычно обходятся дороже, чем выборка сразу нескольких строк, параметр выполнения Connection.execution_options.yield_per настраивает Connection или оператор на использование доступных курсоров на стороне сервера, в то же время настраивая буфер строк фиксированного размера, который будет получать строки с сервера партиями по мере их потребления. Этот параметр может иметь целочисленное положительное значение при использовании метода Connection.execution_options() на Connection или на операторе при использовании метода Executable.execution_options().
Использование этой опции эквивалентно ручной установке опции Connection.execution_options.stream_results, описанной в следующем разделе, а затем вызову метода Result.yield_per() на объекте Result с заданным целочисленным значением. В обоих случаях эффект, который дает эта комбинация, включает:
режим курсоров на стороне сервера выбирается для данного бэкенда, если он доступен и не является поведением по умолчанию для данного бэкенда
по мере получения строк результата, они будут буферизироваться партиями, где размер каждой партии до последней партии будет равен целочисленному аргументу, переданному опции Connection.execution_options.yield_per или методу Result.yield_per(); последняя партия затем буферизируется относительно оставшихся строк, меньших, чем этот размер
Размер раздела по умолчанию, используемый методом Result.partitions(), если он используется, также будет сделан равным этому целочисленному размеру.
Эти три вида поведения проиллюстрированы в примере ниже:
withengine.connect()asconn:withconn.execution_options(yield_per=100).execute(text("select * from table"))asresult:forpartitioninresult.partitions():# partition is an iterable that will be at most 100 itemsforrowinpartition:print(f"{row}")
Приведенный выше пример иллюстрирует комбинацию yield_per=100 вместе с использованием метода Result.partitions() для запуска обработки строк в партиях, соответствующих размеру, получаемых с сервера. Использование Result.partitions() является необязательным, и если Result итерируется напрямую, новая партия строк будет буферизироваться для каждых 100 извлеченных строк. Вызов такого метода, как Result.all(), не должен использоваться, так как это приведет к полной выборке всех оставшихся строк сразу и нарушит цель использования yield_per.
Совет
Объект Result можно использовать в качестве менеджера контекста, как показано выше. При итерации с курсором на стороне сервера это лучший способ обеспечить закрытие объекта Result, даже если в процессе итерации будут возникать исключения.
Добавлено в версии 1.4.40: Добавлено Connection.execution_options.yield_per в качестве опции выполнения на уровне ядра для удобной установки результатов потоковой обработки, размера буфера и размера раздела одновременно, что можно перенести на аналогичный случай использования ORM.
Потоковая передача с динамически растущим буфером с помощью stream_results¶
Когда объект Result, переданный с помощью опции Connection.execution_options.stream_results, итерируется напрямую, строки извлекаются внутренне, используя схему буферизации по умолчанию, которая буферизирует сначала небольшой набор строк, затем все больший и больший буфер при каждом извлечении до предварительно настроенного предела в 1000 строк. Максимальный размер этого буфера может быть изменен с помощью опции выполнения Connection.execution_options.max_row_buffer:
withengine.connect()asconn:withconn.execution_options(stream_results=True,max_row_buffer=100).execute(text("select * from table"))asresult:forrowinresult:print(f"{row}")
Для поддержки многопользовательских приложений, которые распределяют общие наборы таблиц по нескольким схемам, опция выполнения Connection.execution_options.schema_translate_map может быть использована для переназначения набора объектов Table для отображения под разными именами схем без каких-либо изменений.
То есть, имя схемы заменяется нашим переведенным именем. В карте может быть указано любое количество схем 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})
Эта функция действует только в тех случаях, когда имя схемы непосредственно выводится из имени схемы Table или Sequence; она не влияет на методы, в которых строковое имя схемы передается напрямую. По этой схеме, она действует в рамках проверок «может создавать» / «может уничтожать», выполняемых такими методами, как MetaData.create_all() или MetaData.drop_all(), и действует при использовании отражения таблицы, заданной объектом Table. Однако это не влияет на операции, выполняемые над объектом Inspector, поскольку имя схемы передается этим методам в явном виде.
Совет
Чтобы использовать функцию трансляции схемы с ORM Session, установите этот параметр на уровне Engine, затем передайте этот механизм в Session. В Session для каждой транзакции используется новый Connection:
При использовании ORM Session без расширений функция перевода схемы поддерживается только в виде одной карты перевода схемы на сессию. Она не будет работать, если разные карты перевода схемы задаются на основе каждого запроса, так как ORM Session не учитывает текущие значения перевода схемы для отдельных объектов.
SQLAlchemy включает в себя комплексную систему кэширования для компилятора SQL, а также его ORM-вариантов. Эта система кэширования прозрачна в пределах Engine и обеспечивает, что процесс компиляции SQL для данного SQL-компилятора Core или ORM, а также связанные с ним вычисления, которые собирают механику выборки результатов для этого оператора, будут происходить только один раз для этого объекта оператора и всех других с идентичной структурой, в течение всего времени, пока конкретная структура остается в «скомпилированном кэше» движка. Под «объектами утверждений, имеющими идентичную структуру», обычно подразумевается SQL-оператор, который строится внутри функции и создается каждый раз, когда эта функция выполняется:
Приведенный выше оператор сгенерирует SQL, похожий на SELECTid,colFROMtableWHEREcol=:colORDERBYid, отмечая, что хотя значение 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:
Размер кэша может вырасти в 150% от заданного размера, прежде чем он будет сокращен до целевого размера. Таким образом, кэш размером 1200 может вырасти до 1800 элементов, после чего он будет обрезан до 1200.
Размер кэша основан на одной записи для каждого уникального SQL-запроса, созданного на каждом движке. SQL-запросы, генерируемые как ядром, так и ORM, обрабатываются одинаково. Операторы DDL обычно не кэшируются. Для того чтобы определить, что делает кэш, в журнале регистрации движка будут содержаться подробности о поведении кэша, описанные в следующем разделе.
Оценка производительности кэша с помощью протоколирования¶
Указанный выше размер кэша 1200 на самом деле довольно велик. Для небольших приложений, скорее всего, будет достаточно размера 100. Для оценки оптимального размера кэша, при условии наличия достаточного количества памяти на целевом узле, размер кэша должен быть основан на количестве уникальных строк SQL, которые могут быть отображены для используемого целевого механизма. Наиболее целесообразным способом увидеть это является использование эха SQL, которое наиболее непосредственно включается с помощью флага create_engine.echo или с помощью протоколирования Python; см. раздел Настройка ведения журнала о конфигурации протоколирования.
В качестве примера мы рассмотрим журнал, создаваемый следующей программой:
При запуске каждый SQL-оператор, который регистрируется, будет включать значок статистики кэша в скобках слева от переданных параметров. Четыре типа сообщений, которые мы можем увидеть, сводятся к следующему:
[rawsql] - драйвер или конечный пользователь выдал необработанный SQL, используя Connection.exec_driver_sql() - кэширование не применяется
[nokey] - объект оператора является оператором DDL, который не кэшируется, или объект оператора содержит некэшируемые элементы, такие как определяемые пользователем конструкции или произвольно большие предложения VALUES.
[generatedinXs] - утверждение было пропуском кэша и должно было быть скомпилировано, а затем сохранено в кэше. на создание скомпилированной конструкции ушло X секунд. Число X будет в мелких долях секунды.
[cachedsinceXsago] - утверждение было попаданием в кэш и не требовало перекомпиляции. Утверждение было сохранено в кэше с 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 значок читается как [rawsql], что указывает на то, что драйвер отправляет строку Python непосредственно в базу данных с помощью Connection.exec_driver_sql(). Кэширование не применяется к таким утверждениям, поскольку они уже существуют в строковой форме, и ничего не известно о том, какие типы строк результатов будут возвращены, поскольку SQLAlchemy не разбирает строки SQL заранее.
Следующие утверждения, которые мы видим, это утверждения CREATE TABLE:
Для каждого из этих утверждений значок читается как [nokey0.00006s]. Это указывает на то, что в этих двух конкретных утверждениях кэширование не произошло, поскольку DDL-ориентированная конструкция CreateTable не создала ключ кэша. Конструкции DDL обычно не участвуют в кэшировании, потому что они, как правило, не повторяются во второй раз, а DDL также является этапом конфигурирования базы данных, где производительность не так критична.
Значок [nokey] важен еще по одной причине, поскольку он может быть получен для SQL-запросов, которые кэшируются, за исключением некоторых конкретных подконструкций, которые в настоящее время не кэшируются. Примерами этого являются пользовательские элементы SQL, которые не определяют параметры кэширования, а также некоторые конструкции, которые генерируют произвольно длинные и невоспроизводимые строки SQL, основными примерами которых являются конструкция Values, а также при использовании «многозначных вставок» с помощью метода Insert.values().
Пока наш кэш все еще пуст. Однако следующие утверждения будут кэшироваться, сегмент выглядит следующим образом:
Выше мы видим, по сути, две уникальные SQL-строки: "INSERTINTOa(data)VALUES(?)" и "INSERTINTOb(a_id,data)VALUES(?,?)". Поскольку SQLAlchemy использует связанные параметры для всех буквальных значений, даже если эти утверждения повторяются много раз для разных объектов, поскольку параметры разделены, фактическая строка SQL остается неизменной.
Примечание
два вышеприведенных утверждения генерируются процессом ORM, и на самом деле они будут кэшироваться в отдельном кэше, локальном для каждого маппера. Однако механика и терминология одинаковы. В разделе Отключение или использование альтернативного словаря для кэширования некоторых (или всех) утверждений ниже будет описано, как пользовательский код может также использовать альтернативный контейнер кэширования на основе каждого утверждения.
Значок кэширования, который мы видим для первого появления каждого из этих двух утверждений, - [generatedin0.00011s]. Это означает, что утверждение не было в кэше, было скомпилировано в строку за .00011 с и затем было кэшировано. Когда мы видим значок [generated], мы знаем, что это означает, что произошел промах в кэше. Этого следует ожидать при первом появлении определенного оператора. Однако, если наблюдается много новых значков [generated] для долго работающего приложения, которое обычно использует одну и ту же серию SQL-запросов снова и снова, это может быть признаком того, что параметр create_engine.query_cache_size слишком мал. Когда оператор, который был кэширован, затем удаляется из кэша из-за обрезки кэша LRU менее используемых элементов, при следующем использовании он отобразит значок [generated].
Значок кэширования, который мы затем видим для последующих вхождений каждого из этих двух утверждений, выглядит как [cachedsince0.0003533sago]. Это означает, что утверждение было найдено в кэше, и первоначально было помещено в кэш .0003533 секунды назад. Важно отметить, что хотя значки [generated] и [cachedsince] означают количество секунд, они означают разные вещи; в случае [generated] это число является приблизительным отсчетом времени, которое потребовалось для компиляции утверждения, и будет чрезвычайно малым. В случае [cachedsince] это общее время, в течение которого утверждение находилось в кэше. Для приложения, работающего в течение шести часов, это число может быть [cachedsince21600secondsago], и это хорошо. Большое число «кэшировано с тех пор» является признаком того, что эти утверждения не подвергались промахам кэша в течение длительного времени. Утверждения, которые часто имеют низкое число «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-запросов и кэшировать их в определенном словаре:
SQLAlchemy ORM использует вышеупомянутую технику для хранения кэша каждого маппера в рамках процесса «промывки» единицы работы, который отделен от кэша по умолчанию, настроенного на Engine, а также для некоторых запросов загрузчика отношений.
Кэш также можно отключить с помощью этого аргумента, отправив значение None:
# disable caching for this connectionwithengine.connect().execution_options(compiled_cache=None)asconn: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-запросов с различными параметрами.
Для всех сторонних диалектов, которые не поддерживают этот атрибут, в журнале регистрации для такого диалекта будет указано dialectdoesnotsupportcaching.
Если диалект был протестирован на кэширование, и, в частности, компилятор SQL был обновлен, чтобы не отображать любой литерал LIMIT / OFFSET в строке SQL напрямую, авторы диалекта могут применять атрибут следующим образом:
Типичный случай для модификации диалекта следующий.
Пример: Рендеринг LIMIT / OFFSET с параметрами после компиляции¶
В качестве примера, предположим, что диалект переопределяет метод SQLCompiler.limit_clause(), который создает предложение «LIMIT / OFFSET» для оператора SQL, например, так:
Приведенная выше процедура выдает целочисленные значения 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 codedeflimit_clause(self,select,**kw):text=""limit_clause=select._limit_clauseoffset_clause=select._offset_clauseifselect._simple_int_clause(limit_clause):text+=" \n LIMIT %s"%(self.process(limit_clause.render_literal_execute(),**kw))eliflimit_clauseisnotNone:# assuming the DB doesn't support SQL expressions for LIMIT.# Otherwise render here normallyraiseexc.CompileError("dialect 'mydialect' can only render simple integers for LIMIT")ifselect._simple_int_clause(offset_clause):text+=" \n OFFSET %s"%(self.process(offset_clause.render_literal_execute(),**kw))elifoffset_clauseisnotNone:# assuming the DB doesn't support SQL expressions for OFFSET.# Otherwise render here normallyraiseexc.CompileError("dialect 'mydialect' can only render simple integers for OFFSET")returntext
Приведенный выше подход создаст скомпилированный оператор SELECT, который будет выглядеть следующим образом:
Там, где указано выше, индикаторы __[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, она также должна иметь закрывающие переменные, которые важны для всего подхода:
Выше, три вызываемые функции lambda, которые используются для определения структуры оператора SELECT, вызываются ровно один раз, а полученная строка SQL кэшируется в кэше компиляции движка. С этого момента функция run_my_statement() может быть вызвана любое количество раз, а входящие в нее вызываемые переменные lambda не будут вызваны, а будут использоваться только как ключи кэша для получения уже скомпилированного SQL.
Примечание
Важно отметить, что кэширование SQL уже существует, когда система лямбда не используется. Система лямбд лишь добавляет дополнительный уровень сокращения работы на каждый вызываемый SQL-оператор за счет кэширования построения самой SQL-конструкции, а также использования более простого ключа кэша.
Основное внимание в системе лямбда SQL уделяется тому, чтобы никогда не было несоответствия между ключом кэша, созданным для лямбды, и строкой SQL, которую она произведет. LambdaElement и связанные с ним объекты будут запускать и анализировать заданную лямбду, чтобы рассчитать, как она должна кэшироваться при каждом запуске, пытаясь обнаружить любые потенциальные проблемы. Основные рекомендации включают:
Поддерживается любой тип утверждений - хотя предполагается, что конструкции select() являются основным примером использования lambda_stmt(), утверждения DML, такие как insert() и update(), также могут быть использованы:
Случаи прямого использования также поддерживаются - lambda_stmt() может полностью вмещать функциональность ORM и использоваться непосредственно с Session.execute():
Связанные параметры учитываются автоматически - в отличие от предыдущей системы «запеченных запросов» SQLAlchemy, система лямбда SQL учитывает буквенные значения Python, которые становятся связанными параметрами SQL автоматически. Это означает, что даже если данная лямбда выполняется только один раз, значения, которые становятся связанными параметрами, извлекаются из закрытия лямбды при каждом выполнении:
>>> defmy_stmt(x,y):... stmt=lambda_stmt(lambda:select(func.max(x,y)))... returnstmt>>> engine=create_engine("sqlite://",echo=True)>>> withengine.connect()asconn:... 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:defmy_stmt(parameter,thing=False):stmt=lambda_stmt(lambda:select(table))stmt+=(lambdas:s.where(table.c.x>parameter)ifthingelses.where(table.c.y==parameter))returnstmt# **Do** do this:defmy_stmt(parameter,thing=False):stmt=lambda_stmt(lambda:select(table))ifthing:stmt+=lambdas:s.where(table.c.x>parameter)else:stmt+=lambdas:s.where(table.c.y==parameter)returnstmt
Существует целый ряд сбоев, которые могут произойти, если лямбда не производит последовательную конструкцию SQL, и некоторые из них не могут быть тривиально обнаружены прямо сейчас.
Не используйте функции внутри лямбды для создания связанных значений - подход отслеживания связанных значений требует, чтобы фактическое значение, которое будет использоваться в SQL-операторе, локально присутствовало в закрытии лямбды. Это невозможно, если значения генерируются из других функций, и LambdaElement обычно должен вызывать ошибку при такой попытке:
>>> defmy_stmt(x,y):... defget_x():... returnx...... defget_y():... returny...... stmt=lambda_stmt(lambda:select(func.max(get_x(),get_y())))... returnstmt>>> withengine.connect()asconn:... 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 variablesto produce literal values since the lambda SQL system normally extractsbound values without actually invoking the lambda or any functions within it.
Выше, использование get_x() и get_y(), если они необходимы, должно происходить вне лямбды и присваиваться локальной закрывающей переменной:
Избегайте ссылаться на не-SQL конструкции внутри лямбд, поскольку они не кэшируются по умолчанию - этот вопрос относится к тому, как LambdaElement создает ключ кэша из других переменных закрытия в операторе. Для того чтобы обеспечить наилучшую гарантию точного ключа кэша, все объекты, находящиеся в закрытии лямбды, считаются значимыми, и ни один из них не будет считаться подходящим для ключа кэша по умолчанию. Поэтому в следующем примере также будет выдано довольно подробное сообщение об ошибке:
>>> classFoo:... def__init__(self,x,y):... self.x=x... self.y=y>>> defmy_stmt(foo):... stmt=lambda_stmt(lambda:select(func.max(foo.x,foo.y)))... returnstmt>>> withengine.connect()asconn:... print(conn.scalar(my_stmt(Foo(5,10))))Traceback (most recent call last): # ...sqlalchemy.exc.InvalidRequestError: Closure variable named 'foo' inside oflambda callable <code object <lambda> at 0x7fed15f35450, file"<stdin>", line 2> does not refer to a cacheable SQL element, and alsodoes not appear to be serving as a SQL literal bound value based on thedefault SQL expression returned by the function. This variable needs toremain outside the scope of a SQL-generating lambda so that a proper cachekey may be generated from the lambda's state. Evaluate this variableoutside of the lambda, set track_on=[<elements>] to explicitly selectclosure elements to track, or set track_closure_variables=False to excludeclosure variables from being part of the cache key.
Приведенная выше ошибка указывает на то, что LambdaElement не предполагает, что переданный объект Foo будет вести себя одинаково во всех случаях. Он также не предполагает, что может использовать Foo как часть ключа кэша по умолчанию; если бы он использовал объект Foo как часть ключа кэша, то при наличии множества различных объектов Foo он заполнил бы кэш дублирующейся информацией, а также хранил бы длительные ссылки на все эти объекты.
Лучший способ разрешить описанную выше ситуацию - не ссылаться на foo внутри лямбды, а ссылаться на нее вне:
В некоторых ситуациях, если гарантируется, что SQL-структура лямбды никогда не изменится на основе ввода, передать track_closure_variables=False, что отключит любое отслеживание закрывающих переменных, кроме тех, которые используются для связанных параметров:
Существует также возможность добавить объекты в элемент, чтобы явно сформировать часть ключа кэша, используя параметр track_on; использование этого параметра позволяет определенным значениям служить в качестве ключа кэша, а также предотвращает рассмотрение других переменных закрытия. Это полезно для случаев, когда часть конструируемого SQL берет начало от какого-либо контекстного объекта, который может иметь множество различных значений. В приведенном ниже примере первый сегмент оператора SELECT отключит отслеживание переменной foo, в то время как второй сегмент будет явно отслеживать self как часть ключа кэша:
Использование track_on означает, что заданные объекты будут долго храниться во внутреннем кэше лямбды и будут иметь сильные ссылки до тех пор, пока кэш не очистится от этих объектов (по умолчанию используется схема LRU из 1000 записей).
Для понимания некоторых опций и поведения, которые возникают при использовании лямбда-конструкций SQL, полезно понимание системы кэширования.
Система кэширования SQLAlchemy обычно генерирует ключ кэша из заданной конструкции SQL-выражения, создавая структуру, которая представляет все состояние внутри конструкции:
>>> fromsqlalchemyimportselect,column>>> stmt=select(column("q"))>>> cache_key=stmt._generate_cache_key()>>> print(cache_key)# somewhat paraphrasedCacheKey(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». Мы можем заметить, что даже для очень короткого запроса ключ кэша довольно многословен, поскольку он должен представлять все, что может варьироваться о том, что отображается и потенциально выполняется.
Система построения лямбд, напротив, создает другой вид ключа кэша:
>>> fromsqlalchemyimportlambda_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, такие как объекты столбцов, они становятся частью ключа кэша, или если они ссылаются на литеральные значения, которые будут связанными параметрами, они помещаются в отдельный элемент ключа кэша:
Приведенный выше StatementLambdaElement включает две ламбды, обе из которых ссылаются на закрывающую переменную col, поэтому ключ кэша будет представлять оба этих сегмента, а также объект column():
Для серии примеров кэширования «лямбда» со сравнением производительности, смотрите набор тестов «short_selects» в примере производительности Производительность.
Поведение «Вставка многих значений» для операторов INSERT¶
Функция 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», однако имеет те же схемы использования и эквивалентные преимущества в производительности.
Причина, по которой может потребоваться отключить RETURNING для конкретной таблицы, заключается в том, чтобы обойти ограничения, специфичные для бэкенда.
Эта функция имеет два режима работы, которые выбираются прозрачно на основе каждого диалекта, каждого:class:_schema.Table. Один из них - батированный режим, который сокращает количество обходов базы данных, переписывая оператор INSERT в форме:
где выше, оператор организуется против подмножества («партии») входных данных, размер которых определяется бэкендом базы данных, а также количество параметров в каждой партии, чтобы соответствовать известным ограничениям на размер оператора / количество параметров. Затем функция выполняет оператор 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-форма:
Аналогичная форма используется и в 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 столбец, как показано ниже:
importuuidfromsqlalchemyimportColumnfromsqlalchemyimportFetchedValuefromsqlalchemyimportIntegerfromsqlalchemyimportStringfromsqlalchemyimportTablefromsqlalchemyimportUuidmy_table=Table("some_table",metadata,# assume some arbitrary server-side function generates# primary key values, so cannot be tracked by a bulk insertColumn("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:
Хотя значения, генерируемые генератором по умолчанию, должны быть уникальными, фактическое ограничение UNIQUE на вышеупомянутый столбец «sentinel», указываемое параметром unique=True, само по себе является необязательным и может быть опущено, если это нежелательно.
Существует также специальная форма «insert sentinel», которая представляет собой специальный нулевой целочисленный столбец, использующий специальный целочисленный счетчик по умолчанию, который используется только во время операций «insertmanyvalues»; в качестве дополнительного поведения, столбец будет исключать себя из SQL-операторов и наборов результатов и вести себя в основном прозрачным образом. Тем не менее, он должен физически присутствовать в реальной таблице базы данных. Этот стиль Column может быть построен с помощью функции insert_sentinel():
При использовании ORM Declarative доступна версия insert_sentinel(), дружественная к декларативности, под названием orm_insert_sentinel(), которая может быть использована для базового класса или миксина; если упакована с использованием declared_attr(), колонка будет применяться ко всем связанным с таблицей подклассам, в том числе внутри объединенных иерархий наследования:
В приведенном выше примере и «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 наборов параметров в каждый оператор:
Функция «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)
...
Диалекты 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 будет удален и больше не будет использоваться, то все зарегистрированные соединения, на которые он ссылается, также будут полностью закрыты.
Когда программа хочет освободить все оставшиеся зарегистрированные соединения, удерживаемые пулом соединений, и ожидает, что больше не будет подключаться к этой базе данных для любых будущих операций.
Когда программа использует многопроцессорность или 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, непосредственно переданный базовому драйверу (известный как DBAPI) без вмешательства конструкции text(), можно использовать метод Connection.exec_driver_sql():
В некоторых случаях SQLAlchemy не предоставляет обобщенного способа доступа к некоторым функциям DBAPI, таким как вызов хранимых процедур, а также работа с несколькими наборами результатов. В этих случаях целесообразно работать с необработанным DBAPI-соединением напрямую.
Наиболее распространенный способ доступа к необработанному соединению DBAPI - получить его из уже присутствующего объекта Connection напрямую. Он присутствует с помощью атрибута 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.
Вызов хранимых процедур и функций, определяемых пользователем¶
SQLAlchemy поддерживает вызов хранимых процедур и функций, определенных пользователем, несколькими способами. Пожалуйста, обратите внимание, что все DBAPI имеют различные практики, поэтому вы должны обратиться к документации вашей базовой DBAPI для уточнения специфики вашего конкретного использования. Следующие примеры являются гипотетическими и могут не работать с вашим базовым DBAPI.
Для хранимых процедур или функций с особыми синтаксическими особенностями или параметрами потенциально может быть использован уровень DBAPI callproc с вашим DBAPI. Примером такого шаблона является:
Не все 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://», необходимо выполнить следующие действия:
Создайте пакет под названием foodialect.
В пакете должен быть модуль, содержащий класс диалекта, который обычно является подклассом sqlalchemy.engine.default.DefaultDialect. В этом примере допустим, что он называется FooDialect и доступ к его модулю осуществляется через foodialect.dialect.
Точка входа может быть установлена в setup.cfg следующим образом:
Если диалект обеспечивает поддержку определенного DBAPI поверх существующей базы данных, поддерживаемой SQLAlchemy, имя может быть дано с учетом квалификации базы данных. Например, если бы FooDialect был диалектом MySQL, то точка входа могла бы быть установлена следующим образом:
SQLAlchemy также позволяет регистрировать диалект в текущем процессе, минуя необходимость отдельной установки. Используйте функцию register() следующим образом:
Обеспечивает высокоуровневую функциональность для обернутого соединения 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)
Возвращаемый объект является экземпляром RootTransaction. Этот объект представляет собой «область действия» транзакции, которая завершается при вызове метода Transaction.rollback() или Transaction.commit(); объект также работает как менеджер контекста, как показано выше.
Метод Connection.begin() начинает транзакцию, которая обычно начинается в любом случае, когда соединение впервые используется для выполнения оператора. Этот метод может быть использован для вызова события ConnectionEvents.begin() в определенное время или для организации кода в рамках проверки соединения в терминах управляемых контекстом блоков, таких как:
Приведенный выше код принципиально не отличается по своему поведению от следующего кода, в котором не используется Connection.begin(); приведенный ниже стиль называется стилем «фиксация по мере выполнения»:
С точки зрения базы данных, метод Connection.begin() не выдает никакого SQL и никак не изменяет состояние основного соединения DBAPI; в Python DBAPI нет понятия явного начала транзакции.
Возвращаемый объект является экземпляром NestedTransaction, который включает транзакционные методы NestedTransaction.commit() и NestedTransaction.rollback(); для вложенной транзакции эти методы соответствуют операциям «RELEASE SAVEPOINT <name>» и «ROLLBACK TO SAVEPOINT <name>». Имя точки сохранения является локальным для объекта NestedTransaction и генерируется автоматически. Как и любой другой Transaction, NestedTransaction может использоваться в качестве менеджера контекста, как показано выше, который будет «освобождать» или «откатывать» в зависимости от того, была ли операция внутри блока успешной или вызвала исключение.
Вложенные транзакции требуют поддержки SAVEPOINT в базовой базе данных, в противном случае поведение не определено. SAVEPOINT обычно используется для выполнения операций внутри транзакции, которые могут завершиться неудачей, при этом внешняя транзакция продолжается. Например:
fromsqlalchemyimportexcwithengine.begin()asconnection:trans=connection.begin_nested()try:connection.execute(table.insert(),{"username":"sandy"})trans.commit()exceptexc.IntegrityError:# catch for duplicate usernametrans.rollback()# rollback to savepoint# outer transaction continuesconnection.execute(...)
withengine.connect()asconnection:# begin() wasn't calledwithconnection.begin_nested():willauto-"begin()"firstconnection.execute(...)# savepoint is releasedconnection.execute(...)# explicitly commit outer transactionconnection.commit()# can continue working with connection here
Изменено в версии 2.0: Connection.begin_nested() теперь будет участвовать в поведении соединения «autobegin», которое является новым в версии 2.0 / соединения в стиле «будущего» в версии 1.4.
Это приводит к освобождению базовых ресурсов базы данных, то есть соединения DBAPI, на которое ссылается внутреннее соединение. DBAPI-соединение обычно восстанавливается обратно в соединение-держатель Pool, на которое ссылается Engine, породившее это Connection. Любое транзакционное состояние, присутствующее на соединении DBAPI, также безусловно освобождается через метод rollback() соединения DBAPI, независимо от любого объекта Transaction, который может быть непогашен в отношении этого Connection.
Это имеет эффект вызова Connection.rollback(), если выполняется какая-либо транзакция.
После вызова Connection.close()Connection навсегда переходит в закрытое состояние и не допускает дальнейших операций.
Зафиксировать транзакцию, которая в данный момент находится в процессе выполнения.
Этот метод фиксирует текущую транзакцию, если она была начата. Если транзакция не была начата, метод не имеет никакого эффекта, предполагая, что соединение находится в невалидированном состоянии.
Транзакция начинается на Connection автоматически при первом выполнении оператора или при вызове метода Connection.begin().
Базовое соединение DB-API, управляемое этим Соединением.
Это проксированное соединение пула соединений SQLAlchemy, которое затем имеет атрибут _ConnectionFairy.dbapi_connection, ссылающийся на реальное соединение драйвера.
Отсоедините базовое соединение DB-API от пула соединений.
Например:
withengine.connect()asconn: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 будет буквально закрыто, а не возвращено в исходный пул.
Этот метод можно использовать для изоляции остальной части приложения от измененного состояния соединения (например, уровень изоляции транзакции или аналогичный).
Выполняет строковый SQL-запрос на курсоре DBAPI напрямую, без каких-либо шагов компиляции SQL.
Это можно использовать для передачи любой строки непосредственно в метод cursor.execute() используемого DBAPI.
Параметры:
statement – Строка оператора, которая должна быть выполнена. Связанные параметры должны использовать paramstyle базового DBAPI, например, «qmark», «pyformat», «format» и т.д.
parameters – представляют связанные значения параметров, которые будут использоваться при выполнении. Формат один из следующих: словарь именованных параметров, кортеж позиционных параметров или список, содержащий либо словари, либо кортежи для поддержки множественного выполнения.
parameters – параметры, которые будут связаны в операторе. Это может быть либо словарь имен и значений параметров, либо изменяемая последовательность (например, список) словарей. Если передается список словарей, то при выполнении оператора будет использован метод DBAPI cursor.executemany(). Если передается один словарь, будет использоваться метод DBAPI cursor.execute().
execution_options – необязательный словарь опций выполнения, которые будут связаны с выполнением оператора. Этот словарь может содержать подмножество опций, принимаемых Connection.execution_options().
Установка не SQL-опций для соединения, которые вступают в силу во время выполнения.
Этот метод изменяет данный Connectionна месте; возвращаемым значением является тот же объект Connection, на котором был вызван метод. Обратите внимание, что это противоположно поведению методов execution_options на других объектах, таких как Engine.execution_options() и Executable.execution_options(). Это объясняется тем, что многие подобные опции выполнения обязательно изменяют состояние базового соединения DBAPI в любом случае, поэтому нет возможности сохранить эффект такой опции локализованным для «под» соединения.
Изменено в версии 2.0: Метод Connection.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 AutocommitConnection.get_isolation_level() - просмотр текущего акк
no_parameters – Доступно на: Connection, Executable. При True, если конечный список параметров или словарь абсолютно пуст, будет вызывать оператор на курсоре как cursor.execute(statement), не передавая коллекцию параметров вообще. Некоторые DBAPI, такие как psycopg2 и mysql-python, считают знаки процентов значимыми только при наличии параметров; эта опция позволяет генерировать SQL, содержащий знаки процентов (и, возможно, другие символы), нейтральный по отношению к тому, выполняется ли он DBAPI или передается в сценарий, который впоследствии вызывается средствами командной строки.
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:: Перевод имен схем
Возвращает текущий фактический уровень изоляции, который присутствует в базе данных в пределах данного соединения.
Этот атрибут выполняет операцию SQL с базой данных для получения текущего уровня изоляции, поэтому возвращаемое значение является фактическим уровнем на базовом соединении DBAPI, независимо от того, как было установлено это состояние. Это будет один из четырех фактических режимов изоляции READUNCOMMITTED, READCOMMITTED, REPEATABLEREAD, SERIALIZABLE. Он не будет включать настройку уровня изоляции AUTOCOMMIT. Диалекты сторонних разработчиков могут иметь дополнительные настройки уровня изоляции.
Примечание
Этот метод не сообщает об уровне изоляции AUTOCOMMIT, который является отдельной настройкой dbapi, не зависящей от фактического уровня изоляции. Когда используется AUTOCOMMIT, соединение с базой данных все еще имеет «традиционный» режим изоляции, который обычно является одним из четырех значений READUNCOMMITTED, READCOMMITTED, REPEATABLEREAD, SERIALIZABLE.
Сравните с аксессором Connection.default_isolation_level, который возвращает уровень изоляции, присутствующий в базе данных при первоначальном подключении.
Информационный словарь, связанный с базовым соединением DBAPI, на которое ссылается данный Connection, позволяющий ассоциировать с соединением данные, определяемые пользователем.
Данные здесь будут следовать вместе с соединением DBAPI, в том числе после возврата в пул соединений, и снова использоваться в последующих экземплярах Connection.
Аннулировать базовое соединение DBAPI, связанное с этим Connection.
Будет предпринята попытка немедленно закрыть базовое соединение DBAPI; однако если эта операция не удастся, ошибка будет зарегистрирована, но не поднята. Затем соединение будет разорвано независимо от того, удалось ли выполнить close() или нет.
При следующем использовании (где «использование» обычно означает использование метода Connection.execute() или аналогичного), этот Connection попытается получить новое соединение DBAPI, используя услуги Pool в качестве источника соединения (например, «повторное соединение»).
Если транзакция находилась в процессе выполнения (например, был вызван метод Connection.begin()), когда вызывается метод Connection.invalidate(), на уровне DBAPI все состояние, связанное с этой транзакцией, теряется, поскольку соединение DBAPI закрывается. Connection не позволит продолжить повторное соединение, пока объект Transaction не будет завершен вызовом метода Transaction.rollback(); до этого момента любая попытка продолжить использование Connection вызовет ошибку InvalidRequestError. Это сделано для того, чтобы предотвратить случайное продолжение транзакционных операций, несмотря на то, что транзакция была потеряна в результате аннулирования.
Откатить транзакцию, которая в данный момент находится в процессе выполнения.
Этот метод откатывает текущую транзакцию, если она была начата. Если транзакция не была начата, метод не имеет эффекта. Если транзакция была начата и соединение находится в состоянии недействительности, транзакция очищается с помощью этого метода.
Транзакция начинается на Connection автоматически при первом выполнении оператора или при вызове метода Connection.begin().
Набор крючков, предназначенных для дополнения построения объекта Engine на основе имен точек входа в URL.
Цель CreateEnginePlugin - позволить сторонним системам применять слушателей событий на уровне движка, пула и диалекта без необходимости модификации целевого приложения; вместо этого имена плагинов могут быть добавлены в URL базы данных. Целевые приложения для CreateEnginePlugin включают:
инструменты производительности соединений и SQL, например, которые используют события для отслеживания количества проверок и/или времени, затраченного на выполнение утверждений
подключаемые модули, такие как прокси-серверы
Простейший CreateEnginePlugin, который присоединяет регистратор к объекту Engine может выглядеть следующим образом:
importloggingfromsqlalchemy.engineimportCreateEnginePluginfromsqlalchemyimporteventclassLogCursorEventsPlugin(CreateEnginePlugin):def__init__(self,url,kwargs):# consume the parameter "log_cursor_logging_name" from the# URL querylogging_name=url.query.get("log_cursor_logging_name","log_cursor")self.log=logging.getLogger(logging_name)defupdate_url(self,url):"update the URL to one that no longer includes our parameters"returnurl.difference_update_query(["log_cursor_logging_name"])defengine_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)
Плагины регистрируются с помощью точек входа аналогично тому, как это делается для диалектов:
Добавлено в версии 1.2.3: Имена плагинов также могут быть указаны в create_engine() в виде списка
Плагин может потреблять специфические для плагина аргументы из объекта URL, а также из словаря kwargs, который является словарем аргументов, передаваемых вызову create_engine(). «Потребление» этих аргументов включает в себя то, что они должны быть удалены при инициализации плагина, чтобы аргументы не были переданы конструктору Dialect, где они вызовут ошибку ArgumentError, поскольку они не известны диалекту.
Начиная с версии 1.4 SQLAlchemy, аргументы следует продолжать получать из словаря kwargs непосредственно, удаляя значения с помощью метода типа dict.pop. Аргументы из объекта URL должны потребляться путем реализации метода CreateEnginePlugin.update_url(), возвращающего новую копию URL с удаленными специфическими для плагина параметрами:
Изменено в версии 1.4: Объект URL теперь неизменяемый; CreateEnginePlugin, которому нужно изменить URL, должен реализовать новый добавленный метод CreateEnginePlugin.update_url(), который вызывается после создания плагина.
Для миграции постройте плагин следующим образом, проверяя существование метода CreateEnginePlugin.update_url(), чтобы определить, какая версия запущена:
classMyPlugin(CreateEnginePlugin):def__init__(self,url,kwargs):ifhasattr(CreateEnginePlugin,"update_url"):# detect the 1.4 APIself.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 directlyself.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)defupdate_url(self,url):# this method is only called in the 1.4 versionreturnurl.difference_update_query(["my_argument_one","my_argument_two"])
Когда процесс создания движка завершается и создается объект Engine, он снова передается плагину через хук CreateEnginePlugin.engine_created(). В этом хуке в движок могут быть внесены дополнительные изменения, чаще всего связанные с настройкой событий (например, определенных в Основные мероприятия).
Должен быть возвращен новый URL. Этот метод обычно используется для потребления конфигурационных аргументов из URL, которые должны быть удалены, поскольку они не будут распознаны диалектом. Для удаления этих аргументов доступен метод URL.difference_update_query(). Пример см. в docstring по адресу CreateEnginePlugin.
Очистить скомпилированный кэш, связанный с диалектом.
Это относится только к встроенному кэшу, который устанавливается через параметр create_engine.query_cache_size. Это не влияет на кэши словарей, которые были переданы через параметр Connection.execution_options.query_cache.
Connection действует как менеджер контекста Python, поэтому типичное использование этого метода выглядит так:
withengine.connect()asconnection:connection.execute(text("insert into table values ('foo')"))connection.commit()
Как указано выше, после завершения блока соединение «закрывается», а его базовые ресурсы DBAPI возвращаются в пул соединений. Это также имеет эффект отката любой транзакции, которая была начата явно или была начата через autobegin, и вызовет событие ConnectionEvents.rollback(), если транзакция была начата и все еще находится в процессе.
Утилизация пула соединений, используемого этим Engine.
Новый пул соединений создается сразу после ликвидации старого. Предыдущий пул соединений утилизируется либо активно, путем закрытия всех текущих зарегистрированных соединений в этом пуле, либо пассивно, путем потери ссылок на него, но без закрытия соединений. Последняя стратегия больше подходит для инициализатора в развитом процессе Python.
Параметры:
close – если оставить значение по умолчанию True, это приведет к полному закрытию всех текущих проверенных соединений базы данных. Соединения, которые все еще проверяются, **не будут закрыты, однако они больше не будут связаны с этим Engine, поэтому, когда они закрываются по отдельности, в конечном итоге Pool, с которым они связаны, будет собран мусор, и они будут полностью закрыты, если еще не были закрыты при регистрации. Если установлено значение False, предыдущий пул соединений отменяется, а в остальном он никак не затрагивается.
Добавлено в версии 1.4.33: Добавлен параметр Engine.dispose.close, позволяющий заменить пул соединений в дочернем процессе без вмешательства в соединения, используемые родительским процессом.
Возвращает новый Engine, который будет предоставлять Connection объекты с заданными параметрами выполнения.
Возвращенный Engine остается связанным с исходным Engine в том смысле, что он использует тот же пул соединений и другие состояния:
Pool, используемый новым Engine, является тем же экземпляром. Метод Engine.dispose() заменит экземпляр пула соединений для родительского движка, а также этот экземпляр.
Слушатели событий «каскадируются» - это означает, что новый Engine наследует события родителя, а новые события могут быть связаны с новым Engine индивидуально.
Конфигурация протоколирования и имя logging_name копируется из родительского Engine.
Смысл метода Engine.execution_options() заключается в реализации схем, в которых несколько объектов Engine ссылаются на один и тот же пул соединений, но различаются опциями, которые влияют на некоторое поведение на уровне выполнения для каждого механизма. Одним из таких примеров является разбиение на отдельные экземпляры «читатель» и «писатель» Engine, где один Engine имеет более низкие настройки isolation level или даже отключен от транзакций с помощью «autocommit». Пример такой конфигурации приведен в Поддержание нескольких уровней изоляции для одного двигателя.
Другой пример использует пользовательскую опцию shard_id, которая потребляется событием для изменения текущей схемы на соединении с базой данных:
Приведенный выше рецепт иллюстрирует два объекта Engine, каждый из которых будет служить фабрикой для объектов Connection, в которых присутствуют заранее установленные опции выполнения «shard_id». Обработчик событий ConnectionEvents.before_cursor_execute() интерпретирует эту опцию выполнения, чтобы выдать оператор MySQL use для переключения баз данных перед выполнением оператора, в то же время отслеживая, какую базу данных мы установили с помощью словаря Connection.info.
Возвращает «сырое» соединение DBAPI из пула соединений.
Возвращаемый объект представляет собой проксированную версию объекта соединения DBAPI, используемого используемым базовым драйвером. Объект будет иметь все то же поведение, что и реальное соединение DBAPI, за исключением того, что его метод close() приведет к возврату соединения в пул, а не к реальному закрытию.
Этот метод обеспечивает прямой доступ к соединению DBAPI для особых ситуаций, когда API, предоставляемый Connection, не нужен. Когда объект Connection уже присутствует, соединение DBAPI доступно с помощью аксессора Connection.connection.
Обновление словаря параметров выполнения по умолчанию этого Engine.
Заданные ключи/значения в **opt добавляются к опциям выполнения по умолчанию, которые будут использоваться для всех соединений. Начальное содержимое этого словаря может быть отправлено через параметр execution_options в create_engine().
Этот объект существует исключительно для передачи в событие DialectEvents.handle_error(), поддерживая интерфейс, который может быть расширен без обратной несовместимости.
Это имеет место для операций выполнения оператора, но не для таких операций, как начало/завершение транзакции. Он также отсутствует, когда исключение было вызвано до того, как ExecutionContext мог быть построен.
Представляет, должны ли все соединения в пуле быть аннулированы, когда действует условие «разъединение».
Установка этого флага в False в рамках события DialectEvents.handle_error() приведет к тому, что вся коллекция соединений в пуле не будет аннулирована во время разъединения; только текущее соединение, которое является предметом ошибки, будет фактически аннулировано.
Этот флаг предназначен для пользовательских схем обработки разъединений, когда аннулирование других соединений в пуле должно выполняться на основе других условий или даже на основе каждого соединения.
SQLAlchemy будет использовать этот флаг, чтобы определить, следует ли впоследствии аннулировать соединение. То есть, присвоив этот флаг, можно вызвать событие «разъединение», которое впоследствии приведет к аннулированию соединения и пула, или предотвратить его, изменив этот флаг.
Примечание
Обработчик «pre_ping» пула, включенный с помощью параметра create_engine.pool_pre_ping, не обращается к этому событию, прежде чем решить, что «ping» вернул false, в отличие от получения необработанной ошибки. Для данного случая использования используется параметр legacy recipe based on engine_connect() may be used. Будущий API позволит более полно настроить механизм обнаружения «разъединения» для всех функций.
sqlalchemy.exc.StatementError, который обертывает оригинал и будет поднят, если обработка исключений не будет обойдена событием.
Может быть None, так как не все типы исключений оборачиваются SQLAlchemy. Для исключений на уровне DBAPI, которые подклассифицируют класс Error от dbapi, это поле всегда будет присутствовать.
При использовании NestedTransaction семантика «begin» / «commit» / «rollback» следующая:
операция «begin» соответствует команде «BEGIN SAVEPOINT», где точке сохранения присваивается явное имя, которое является частью состояния этого объекта.
Обоснование для имитации семантики внешней транзакции в терминах точек сохранения, чтобы код мог работать с транзакцией «точки сохранения» и «внешней» транзакцией независимо друг от друга.
При использовании 2.0 style в Connection также применяется поведение «автозапуска», которое будет создавать новый RootTransaction всякий раз, когда соединение в нетранзакционном состоянии используется для передачи команд на соединение DBAPI. Область применения RootTransaction в стиле 2.0 можно контролировать с помощью методов Connection.commit() и Connection.rollback().
fromsqlalchemyimportcreate_engineengine=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():
withconnection.begin():connection.execute(text("insert into x (a, b) values (1, 2)"))
IteratorResult, который работает от вызываемого итератора.
Данный аргумент chunks является функцией, которой задается количество строк, возвращаемых в каждом чанке, или None для всех строк. Функция должна вернуть непотребляемый итератор списков, каждый список запрашиваемого размера.
Функция может быть вызвана в любое время снова, в этом случае она должна продолжить работу с того же набора результатов, но скорректировать размер чанка, как указано.
Настройте стратегию выборки строк на выборку num строк за один раз.
Это влияет на базовое поведение результата при итерации по объекту результата или при использовании таких методов, как Result.fetchone(), которые возвращают по одной строке за раз. Данные из базового курсора или другого источника данных будут буферизироваться в памяти до этого количества строк, а затем буферизированная коллекция будет выдаваться по одной строке за раз или столько строк, сколько запрошено. Каждый раз, когда буфер очищается, он обновляется до этого количества строк или столько, сколько строк осталось, если осталось меньше.
Метод Result.yield_per() обычно используется в сочетании с опцией выполнения Connection.execution_options.stream_results, которая позволит используемому диалекту базы данных использовать курсор на стороне сервера, если DBAPI поддерживает определенный режим «курсора на стороне сервера» отдельно от режима работы по умолчанию.
Результат, представляющий состояние курсора DBAPI.
Изменено в версии 1.4: Класс CursorResult` заменяет предыдущий интерфейс ResultProxy. Эти классы основаны на API вызова Result, который предоставляет обновленную модель использования и фасад вызова для SQLAlchemy Core и SQLAlchemy ORM.
Возвращает строки базы данных с помощью класса Row, который предоставляет дополнительные возможности API и поведение поверх необработанных данных, возвращаемых DBAPI. С помощью фильтров, таких как метод Result.scalars(), могут быть возвращены и другие типы объектов.
Это закрывает курсор DBAPI, соответствующий выполнению оператора, если он еще присутствует. Обратите внимание, что курсор DBAPI автоматически освобождается, когда CursorResult исчерпывает все доступные строки. CursorResult.close() является необязательным методом, за исключением случаев, когда отбрасывается CursorResult, в котором еще есть дополнительные строки, ожидающие выборки.
После вызова этого метода уже нельзя обращаться к методам fetch, которые при последующем использовании будут вызывать ошибку ResourceClosedError.
Определите столбцы, которые должны быть возвращены в каждой строке.
Этот метод можно использовать для ограничения возвращаемых столбцов, а также для их переупорядочивания. Заданный список выражений обычно представляет собой ряд целых чисел или строковых имен ключей. Это также могут быть соответствующие объекты ColumnElement, которые соответствуют заданной конструкции оператора.
Изменено в версии 2.0: Из-за ошибки в версии 1.4 метод Result.columns() имел некорректное поведение, когда вызов метода с одним индексом приводил к тому, что объект Result выдавал скалярные значения, а не объекты Row. В версии 2.0 это поведение было исправлено, и теперь вызов метода Result.columns() с одним индексом приводит к созданию объекта Result, который продолжает выдавать объекты Row, включающие только один столбец.
*col_expressions – указывает на возвращаемые столбцы. Элементами могут быть целочисленные индексы строк, строковые имена столбцов или соответствующие объекты ColumnElement, соответствующие конструкции select.
Этот метод предусмотрен для обратной совместимости с SQLAlchemy 1.x.x.
Чтобы получить только первую строку результата, используйте метод Result.first(). Для итерации по всем строкам используйте непосредственно объект Result.
Результат:
объект Row, если фильтры не применяются, или None, если не осталось ни одной строки.
Получить первую строку или None, если строка отсутствует.
Закрывает набор результатов и отбрасывает оставшиеся строки.
Примечание
По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar() или комбинируйте Result.scalars() и Result.first().
Кроме того, в отличие от поведения унаследованного метода ORM Query.first(), не применяется ограничение к SQL-запросу, который был вызван для создания этого Result; для драйвера DBAPI, который буферизирует результаты в памяти перед выдачей строк, все строки будут отправлены в процесс Python, и все, кроме первой строки, будут отброшены.
Возвращает вызываемый объект, который при вызове будет создавать копии этого Result.
Возвращаемый вызываемый объект является экземпляром FrozenResult.
Используется для кэширования набора результатов. Метод должен быть вызван на результате, когда он не был поглощен, и вызов метода полностью поглотит результат. Когда FrozenResult извлекается из кэша, его можно вызывать любое количество раз, и каждый раз он будет создавать новый объект Result по сохраненному набору строк.
Возвращает первичный ключ для только что вставленной строки.
Возвращаемым значением является объект Row, представляющий именованный кортеж значений первичного ключа в том порядке, в котором столбцы первичного ключа настроены в источнике Table.
Этот аксессор применяется только к однорядным конструкциям insert(), в которых не было явно указано Insert.returning(). Поддержка многорядных вставок, пока еще недоступная для большинства бэкендов, будет доступна с помощью аксессора CursorResult.inserted_primary_key_rows.
Обратите внимание, что столбцы первичного ключа, в которых указано условие server_default или которые иначе не квалифицируются как «автоинкрементные» столбцы (см. примечания в Column), и которые были созданы с использованием значения по умолчанию на стороне базы данных, будут отображаться в этом списке как None, если бэкенд не поддерживает «возврат» и оператор вставки выполняется с включенным «неявным возвратом».
Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией insert().
Возвращает значение 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 «помощник быстрого выполнения» будет обобщена для других диалектов, что позволит использовать этот аксессор более широко.
True, если этот CursorResult является результатом выполнения скомпилированной конструкции insert() языка выражений.
Если значение True, это означает, что атрибут inserted_primary_key доступен, при условии, что утверждение не включает определенную пользователем конструкцию «returning».
Возвращает итерируемое представление, которое дает ключи строк, которые были бы представлены каждым Row.
Ключи могут представлять собой метки столбцов, возвращаемых оператором core, или имена классов orm, возвращаемых выполнением orm.
Представление также можно проверить на содержание ключей с помощью оператора Python in, который будет проверять как строковые ключи, представленные в представлении, так и альтернативные ключи, такие как объекты столбцов.
Изменено в версии 1.4: возвращается объект представления ключа, а не обычный список.
Это специфический метод DBAPI, и он работает только для тех бэкендов, которые его поддерживают, для утверждений, где он уместен. Его поведение не является последовательным для разных бэкендов.
Использование этого метода обычно не требуется при использовании конструкций выражения insert(); атрибут CursorResult.inserted_primary_key предоставляет кортеж значений первичного ключа для вновь вставленной строки, независимо от бэкенда базы данных.
Объедините этот Result с другими совместимыми объектами результата.
Возвращаемый объект является экземпляром MergedResult, который будет состоять из итераторов от заданных объектов результата.
Новый результат будет использовать метаданные из этого объекта результата. Последующие объекты результата должны быть против идентичного набора метаданных результата / курсора, иначе поведение не определено.
Вызывает NoResultFound, если результат не возвращает ни одной строки, или MultipleResultsFound, если будет возвращено несколько строк.
Примечание
По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar_one() или комбинируйте Result.scalars() и Result.one().
Каждый список будет иметь заданный размер, за исключением последнего выдаваемого списка, который может иметь небольшое количество строк. Пустые списки выдаваться не будут.
Объект 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(), которое может быть специфичным для бэкенда и не вполне определенным.
Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией insert() или update().
Вызывает InvalidRequestError, если выполняемый оператор не является скомпилированной конструкцией выражения или не является конструкцией insert() или update().
Возвращает список строк, каждая из которых содержит значения столбцов по умолчанию, которые были получены с помощью функции ValuesBase.return_defaults().
В целом, значение CursorResult.returns_rows всегда должно быть синонимом того, имел ли курсор DBAPI атрибут .description, указывающий на наличие колонок результатов, при этом следует отметить, что курсор, возвращающий ноль строк, все равно имеет .description, если был выпущен оператор возврата строк.
Этот атрибут должен иметь значение True для всех результатов, которые относятся к операторам SELECT, а также для DML-операторов INSERT/UPDATE/DELETE, которые используют RETURNING. Для операторов INSERT/UPDATE/DELETE, не использующих RETURNING, значение обычно будет False, однако существуют некоторые специфические для диалекта исключения, например, при использовании диалекта MSSQL / pyodbc SELECT выдается inline для получения вставленного значения первичного ключа.
Этот атрибут возвращает количество строк сопоставленных, что не обязательно совпадает с количеством строк, которые были фактически изменены - например, оператор UPDATE может не иметь никаких чистых изменений в данной строке, если заданные значения SET совпадают с теми, которые уже присутствуют в строке. Такой ряд будет сопоставлен, но не изменен. В бэкендах, поддерживающих оба стиля, таких как MySQL, rowcount по умолчанию настроен на возврат количества совпадений во всех случаях.
CursorResult.rowcount полезен только в сочетании с оператором UPDATE или DELETE. Вопреки тому, что говорит Python DBAPI, он не возвращает количество доступных строк из результатов оператора SELECT, поскольку DBAPI не может поддерживать эту функциональность, когда строки не буферизованы.
Возвращает объект фильтрации 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.
Этот метод предназначен для ORM SQLAlchemy и не предназначен для общего использования.
«горизонтально сращивает» означает, что для каждой строки в первом и втором наборе результатов создается новая строка, конкатенирующая эти две строки вместе, которая затем становится новой строкой. Входящий CursorResult должен иметь одинаковое количество строк. Обычно ожидается, что оба набора результатов будут иметь одинаковый порядок сортировки, так как строки результата объединяются на основе их положения в результате.
Предполагаемый сценарий использования здесь заключается в том, чтобы несколько операторов INSERT…RETURNING (которые определенно должны быть отсортированы) в разных таблицах могли дать один результат, который выглядит как JOIN этих двух таблиц.
Этот метод предназначен для ORM SQLAlchemy и не предназначен для общего использования.
«вертикально сращивает» означает, что строки данного результата добавляются к строкам результата данного курсора. Входящий CursorResult должен иметь строки, которые представляют собой идентичный список столбцов в идентичном порядке, как и в данном CursorResult.
Применить фильтр типизации «типизированный кортеж» к возвращаемым строкам.
Этот метод возвращает тот же объект Result во время выполнения, однако аннотируется как возвращающий объект TupleResult, что укажет инструментам типизации PEP 484, что возвращаются простые типизированные экземпляры Tuple, а не строки. Это позволяет распаковывать кортежи и __getitem__ получать доступ к Row объектам с типизацией, для тех случаев, когда вызываемый оператор содержит информацию о типизации.
Применить уникальную фильтрацию к объектам, возвращаемым этим Result.
Когда этот фильтр применяется без аргументов, возвращаемые строки или объекты фильтруются таким образом, что каждая строка возвращается уникальной. Алгоритм, используемый для определения уникальности, по умолчанию представляет собой хэширование Python идентификатора всего кортежа. В некоторых случаях может использоваться специализированная схема хэширования для каждого объекта, например, при использовании ORM применяется схема, которая работает против идентичности первичного ключа возвращаемых объектов.
Уникальный фильтр применяется после всех других фильтров, что означает, что если возвращаемые столбцы были уточнены с помощью таких методов, как Result.columns() или Result.scalars(), уникальность применяется только к возвращаемому столбцу или столбцам. Это происходит независимо от порядка, в котором эти методы были вызваны на объекте Result.
Уникальный фильтр также изменяет вычисления, используемые для таких методов, как Result.fetchmany() и Result.partitions(). При использовании Result.unique() эти методы будут продолжать выдавать запрошенное количество строк или объектов после применения уникализации. Однако это обязательно влияет на поведение буферизации базового курсора или источника данных, так что может потребоваться несколько вызовов cursor.fetchmany() для накопления достаточного количества объектов, чтобы обеспечить уникальную коллекцию запрашиваемого размера.
Параметры:
strategy – вызываемый объект, который будет применяться к строкам или объектам, по которым выполняется итерация, и который должен возвращать объект, представляющий уникальное значение строки. Для хранения этих уникальностей используется Python set(). Если он не передан, используется стратегия уникальности по умолчанию, которая, возможно, была собрана источником этого объекта Result.
Настройте стратегию выборки строк на выборку num строк за один раз.
Это влияет на базовое поведение результата при итерации по объекту результата или при использовании таких методов, как Result.fetchone(), которые возвращают по одной строке за раз. Данные из базового курсора или другого источника данных будут буферизироваться в памяти до этого количества строк, а затем буферизированная коллекция будет выдаваться по одной строке за раз или столько строк, сколько запрошено. Каждый раз, когда буфер очищается, он обновляется до этого количества строк или столько, сколько строк осталось, если осталось меньше.
Метод Result.yield_per() обычно используется в сочетании с опцией выполнения Connection.execution_options.stream_results, которая позволит используемому диалекту базы данных использовать курсор на стороне сервера, если DBAPI поддерживает определенный режим «курсора на стороне сервера» отдельно от режима работы по умолчанию.
Добавлено в версии 1.4: Объект Result предоставляет полностью обновленную модель использования и фасад вызовов для SQLAlchemy Core и SQLAlchemy ORM. В Core он составляет основу объекта CursorResult, который заменяет предыдущий интерфейс ResultProxy. При использовании ORM обычно используется объект более высокого уровня под названием ChunkedIteratorResult.
Примечание
В SQLAlchemy 1.4 и выше этот объект используется для результатов ORM, возвращаемых Session.execute(), которые могут давать экземпляры сопоставленных с ORM объектов по отдельности или в составе кортежей. Обратите внимание, что объект Result не дедуплицирует экземпляры или строки автоматически, как в случае с устаревшим объектом Query. Для дедупликации экземпляров или строк в Python используйте метод-модификатор Result.unique().
Поведение этого метода зависит от реализации и не реализовано по умолчанию. Метод обычно должен завершать ресурсы, используемые объектом 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.
Определите столбцы, которые должны быть возвращены в каждой строке.
Этот метод можно использовать для ограничения возвращаемых столбцов, а также для их переупорядочивания. Заданный список выражений обычно представляет собой ряд целых чисел или строковых имен ключей. Это также могут быть соответствующие объекты ColumnElement, которые соответствуют заданной конструкции оператора.
Изменено в версии 2.0: Из-за ошибки в версии 1.4 метод Result.columns() имел некорректное поведение, когда вызов метода с одним индексом приводил к тому, что объект Result выдавал скалярные значения, а не объекты Row. В версии 2.0 это поведение было исправлено, и теперь вызов метода Result.columns() с одним индексом приводит к созданию объекта Result, который продолжает выдавать объекты Row, включающие только один столбец.
*col_expressions – указывает на возвращаемые столбцы. Элементами могут быть целочисленные индексы строк, строковые имена столбцов или соответствующие объекты ColumnElement, соответствующие конструкции select.
Этот метод предусмотрен для обратной совместимости с SQLAlchemy 1.x.x.
Чтобы получить только первую строку результата, используйте метод Result.first(). Для итерации по всем строкам используйте непосредственно объект Result.
Результат:
объект Row, если фильтры не применяются, или None, если не осталось ни одной строки.
Получить первую строку или None, если строка отсутствует.
Закрывает набор результатов и отбрасывает оставшиеся строки.
Примечание
По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar() или комбинируйте Result.scalars() и Result.first().
Кроме того, в отличие от поведения унаследованного метода ORM Query.first(), не применяется ограничение к SQL-запросу, который был вызван для создания этого Result; для драйвера DBAPI, который буферизирует результаты в памяти перед выдачей строк, все строки будут отправлены в процесс Python, и все, кроме первой строки, будут отброшены.
Возвращает вызываемый объект, который при вызове будет создавать копии этого Result.
Возвращаемый вызываемый объект является экземпляром FrozenResult.
Используется для кэширования набора результатов. Метод должен быть вызван на результате, когда он не был поглощен, и вызов метода полностью поглотит результат. Когда FrozenResult извлекается из кэша, его можно вызывать любое количество раз, и каждый раз он будет создавать новый объект Result по сохраненному набору строк.
Возвращает итерируемое представление, которое дает ключи строк, которые были бы представлены каждым Row.
Ключи могут представлять собой метки столбцов, возвращаемых оператором core, или имена классов orm, возвращаемых выполнением orm.
Представление также можно проверить на содержание ключей с помощью оператора Python in, который будет проверять как строковые ключи, представленные в представлении, так и альтернативные ключи, такие как объекты столбцов.
Изменено в версии 1.4: возвращается объект представления ключа, а не обычный список.
Объедините этот Result с другими совместимыми объектами результата.
Возвращаемый объект является экземпляром MergedResult, который будет состоять из итераторов от заданных объектов результата.
Новый результат будет использовать метаданные из этого объекта результата. Последующие объекты результата должны быть против идентичного набора метаданных результата / курсора, иначе поведение не определено.
Вызывает NoResultFound, если результат не возвращает ни одной строки, или MultipleResultsFound, если будет возвращено несколько строк.
Примечание
По умолчанию этот метод возвращает одну строку, например, кортеж. Чтобы вернуть точно одно единственное скалярное значение, то есть первый столбец первой строки, используйте метод Result.scalar_one() или комбинируйте Result.scalars() и Result.one().
Каждый список будет иметь заданный размер, за исключением последнего выдаваемого списка, который может иметь небольшое количество строк. Пустые списки выдаваться не будут.
Объект 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(), которое может быть специфичным для бэкенда и не вполне определенным.
Возвращает объект фильтрации 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.
Применить фильтр типизации «типизированный кортеж» к возвращаемым строкам.
Этот метод возвращает тот же объект Result во время выполнения, однако аннотируется как возвращающий объект TupleResult, что укажет инструментам типизации PEP 484, что возвращаются простые типизированные экземпляры Tuple, а не строки. Это позволяет распаковывать кортежи и __getitem__ получать доступ к Row объектам с типизацией, для тех случаев, когда вызываемый оператор содержит информацию о типизации.
Применить уникальную фильтрацию к объектам, возвращаемым этим Result.
Когда этот фильтр применяется без аргументов, возвращаемые строки или объекты фильтруются таким образом, что каждая строка возвращается уникальной. Алгоритм, используемый для определения уникальности, по умолчанию представляет собой хэширование Python идентификатора всего кортежа. В некоторых случаях может использоваться специализированная схема хэширования для каждого объекта, например, при использовании ORM применяется схема, которая работает против идентичности первичного ключа возвращаемых объектов.
Уникальный фильтр применяется после всех других фильтров, что означает, что если возвращаемые столбцы были уточнены с помощью таких методов, как Result.columns() или Result.scalars(), уникальность применяется только к возвращаемому столбцу или столбцам. Это происходит независимо от порядка, в котором эти методы были вызваны на объекте Result.
Уникальный фильтр также изменяет вычисления, используемые для таких методов, как Result.fetchmany() и Result.partitions(). При использовании Result.unique() эти методы будут продолжать выдавать запрошенное количество строк или объектов после применения уникализации. Однако это обязательно влияет на поведение буферизации базового курсора или источника данных, так что может потребоваться несколько вызовов cursor.fetchmany() для накопления достаточного количества объектов, чтобы обеспечить уникальную коллекцию запрашиваемого размера.
Параметры:
strategy – вызываемый объект, который будет применяться к строкам или объектам, по которым выполняется итерация, и который должен возвращать объект, представляющий уникальное значение строки. Для хранения этих уникальностей используется Python set(). Если он не передан, используется стратегия уникальности по умолчанию, которая, возможно, была собрана источником этого объекта Result.
Настройте стратегию выборки строк на выборку num строк за один раз.
Это влияет на базовое поведение результата при итерации по объекту результата или при использовании таких методов, как Result.fetchone(), которые возвращают по одной строке за раз. Данные из базового курсора или другого источника данных будут буферизироваться в памяти до этого количества строк, а затем буферизированная коллекция будет выдаваться по одной строке за раз или столько строк, сколько запрошено. Каждый раз, когда буфер очищается, он обновляется до этого количества строк или столько, сколько строк осталось, если осталось меньше.
Метод Result.yield_per() обычно используется в сочетании с опцией выполнения Connection.execution_options.stream_results, которая позволит используемому диалекту базы данных использовать курсор на стороне сервера, если DBAPI поддерживает определенный режим «курсора на стороне сервера» отдельно от режима работы по умолчанию.
Особое ограничение ScalarResult заключается в том, что у него нет метода fetchone(); поскольку семантика fetchone() заключается в том, что значение None указывает на отсутствие результатов, это несовместимо с ScalarResult, поскольку нет способа отличить None как значение строки от None как индикатора. Используйте next(result) для получения значений по отдельности.
Возвращает итерируемое представление, которое дает ключи строк, которые были бы представлены каждым Row.
Ключи могут представлять собой метки столбцов, возвращаемых оператором core, или имена классов orm, возвращаемых выполнением orm.
Представление также можно проверить на содержание ключей с помощью оператора Python in, который будет проверять как строковые ключи, представленные в представлении, так и альтернативные ключи, такие как объекты столбцов.
Изменено в версии 1.4: возвращается объект представления ключа, а не обычный список.
Объект Row представляет строку результата базы данных. В SQLAlchemy серии 1.x он обычно ассоциируется с объектом CursorResult, однако начиная с SQLAlchemy 1.4 он также используется ORM для кортежей результатов.
Объект Row стремится действовать как можно больше похоже на именованный кортеж Python. Для поведения отображения (т.е. словаря) в строке, такого как проверка на содержание ключей, обратитесь к атрибуту Row._mapping.
Этот объект предоставляет последовательный интерфейс отображения (т.е. словарь) Python для данных, содержащихся в строке. Сам по себе Row ведет себя как именованный кортеж.
Во время выполнения этот метод возвращает «self»; объект Row уже является именованным кортежем. Однако на уровне типизации, если этот Row типизирован, возвращаемый тип «кортеж» будет типом данных PEP 484Tuple, который содержит информацию о типизации отдельных элементов, поддерживая типизированную распаковку и доступ к атрибутам.
RowMapping предоставляет доступ к содержимому строки через Python mapping (т.е. словарь). Это включает поддержку проверки содержания определенных ключей (строковых имен столбцов или объектов), а также итерации ключей, значений и элементов:
Добавлено в версии 1.4: Объект RowMapping заменяет доступ, подобный отображению, ранее предоставляемый строкой результата базы данных, которая теперь стремится вести себя в основном как именованный кортеж.