Очистить соединение с базой данных с помощью SQLAlchemy в Pandas - PullRequest
0 голосов
/ 04 июля 2018

С помощью Pandas я очень легко могу читать данные из базы данных в фрейм данных:

from sqlalchemy import create_engine
import pandas


query = 'SELECT * FROM Table_Name;'
engine = create_engine('...')

df = pandas.read_sql_query(query, engine)

print(df.head())

Я хотел бы убедиться, что после выполнения .read_sql_query() соединение не остается открытым, не имеет значения, был ли запрос успешным или возникло исключение.

Я сейчас:

  • Использование функции для ограничения объема двигателя. Я ожидаю вызывать эту функцию только раз в полчаса, поэтому я не возражаю воссоздавать движок, если это помогает обеспечить очистку / закрытие / сборку мусора.
  • Отключение пула с помощью poolclass=NullPool.
  • Наконец звонит engine.disponse().

Вот так:

from sqlalchemy import create_engine
from sqlalchemy.pool import NullPool
import pandas


def get_data():
    query = 'SELECT * FROM Table_Name;'
    try:
        engine = create_engine('...', poolclass=NullPool)
        df = pandas.read_sql_query(query, engine)
    finally:
        engine.dispose()
    return df


print(get_data().head())

Есть ли лучший способ?

1 Ответ

0 голосов
/ 09 июля 2018

Фон:

При использовании 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()

Но для простых случаев использования, таких как пример кода, я думаю, что оба способа одинаково чисты и просты для очистки ресурсов ввода-вывода БД.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...