Фон:
При использовании sqlalchemy
с методом панд read_sql_query(query, con)
будет создан объект SQLDatabase
с атрибутом connectable
до self.connectable.execute(query)
. И SQLDatabase.connectable
инициализируется как con
, пока это экземпляр sqlalchemy.engine.Connectable
(т.е. Engine
и Connection
).
Случай I: при передаче Engine
объекта как con
Как пример кода в вашем вопросе:
from sqlalchemy import create_engine
import pandas as pd
engine = create_engine('...')
df = pd.read_sql_query(query, con=engine)
Внутренне, панды просто используют result = engine.execute(query)
, , что означает :
Где выше, метод execute()
самостоятельно получает новый Connection
, выполняет оператор с этим объектом и возвращает ResultProxy
. В этом случае ResultProxy содержит специальный флаг, известный как close_with_result
, который указывает, что, когда его базовый курсор DBAPI закрыт, сам объект Connection
также закрывается, что снова возвращает соединение DBAPI в пул соединений. , высвобождение транзакционных ресурсов.
В этом случае вам не нужно беспокоиться о самом Connection
, который автоматически закрывается, но при этом пул соединений будет engine
.
Таким образом, вы можете отключить пул, используя:
engine = create_engine('...', poolclass=NullPool)
или dispose
двигатель полностью с engine.dispose()
в конце.
Но, следуя документу Engine Disposal (последний абзац) , эти два являются альтернативными, вам не нужно использовать их одновременно. Так что в этом случае для простого одноразового использования read_sql_query
и очистки, я думаю, этого должно быть достаточно:
# Clean up entirely after every query.
engine = create_engine('...')
df = pd.read_sql_query(query, con=engine)
engine.dispose()
Случай II: при передаче Connection
объект как con
:
connection = engine.connect()
print(connection.closed) # False
df = pd.read_sql_query(query, con=connection)
print(connection.closed) # False again
# do_something_else(connection)
connection.close()
print(connection.closed) # True
engine.dispose()
Это следует делать всякий раз, когда вы хотите лучше контролировать атрибуты соединения, когда оно закрывается и т. Д. Например, очень важным примером импорта является Transaction
, который позволяет вам решить, когда фиксировать ваши изменения в база данных. ( из этого ответа )
Но с пандами у нас нет контроля внутри read_sql_query
, единственная полезность connection
в том, что он позволяет вам делать больше полезных вещей, прежде чем мы явно закроем его.
В общем и целом:
Я думаю, что я хотел бы использовать следующий шаблон, который дает мне больше контроля над соединениями и оставляет в будущем расширяемость:
engine = create_engine('...')
# Context manager makes sure the `Connection` is closed safely and implicitly
with engine.connect() as conn:
df = pd.read_sql_query(query, conn)
print(conn.in_transaction()) # False
# do_something_with(conn)
trans = conn.begin()
print(conn.in_transaction()) # True
# do_whatever_with(trans)
print(conn.closed) # False
print('Is Connection with-OUT closed?', conn.closed) # True
engine.dispose()
Но для простых случаев использования, таких как пример кода, я думаю, что оба способа одинаково чисты и просты для очистки ресурсов ввода-вывода БД.