Подключение к базе данных Python Закрыть

Использование приведенного ниже кода оставляет у меня открытое соединение, как закрыть?

import pyodbc
conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 

csr = conn.cursor()  
csr.close()
del csr

person Merlin    schedule 23.09.2010    source источник


Ответы (6)


Соединения имеют метод close, как указано в PEP-249 ( Спецификация API базы данных Python v2.0):

import pyodbc
conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 

csr = conn.cursor()  
csr.close()
conn.close()     #<--- Close the connection

Поскольку pyodbc соединение и курсор оба являются менеджерами контекста, в настоящее время было бы удобнее (и предпочтительнее) записать это как:

import pyodbc
conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 
with conn:
    crs = conn.cursor()
    do_stuff
    # conn.commit() will automatically be called when Python leaves the outer `with` statement
    # Neither crs.close() nor conn.close() will be called upon leaving the `with` statement!! 

Подробнее см. https://github.com/mkleehammer/pyodbc/issues/43. почему conn.close() не вызывается.

Обратите внимание, что в отличие от исходного кода, это приводит к вызову conn.commit(). Используйте внешний оператор with, чтобы контролировать, когда вы хотите, чтобы commit вызывался.


Также обратите внимание, что независимо от того, используете ли вы операторы with, согласно документам ,

Соединения автоматически закрываются при удалении (как правило, когда они выходят за рамки), поэтому обычно вам не нужно вызывать [conn.close()], но вы можете явно закрыть соединение, если хотите.

и аналогично для курсоров (выделено мной):

Курсоры автоматически закрываются при удалении (обычно когда они выходят за пределы области видимости), поэтому вызов [csr.close()] обычно не требуется.

person unutbu    schedule 23.09.2010
comment
Это лучшая практика? Чтобы вручную закрыть курсор, затем удалить его, затем закрыть соединение? - person Dustin Michels; 24.08.2018
comment
Я также хотел бы знать ответ на вопросы @DustinMichels. - person Reed Jessen; 01.02.2019
comment
Согласно документам, соединения автоматически закрываются при их удалении (обычно когда они выходят за рамки), поэтому обычно вам не нужно вызывать это, но вы можете явно закрыть соединение, если хотите. И аналогично для курсоров. - person unutbu; 01.02.2019
comment
@unutbu Не могли бы вы подробнее рассказать, зачем нужен del? Разве close уже не должен закрывать курсор? Экспериментально я мог видеть, что без del, даже если вызов conn.close(), запуск sp_who2 по-прежнему показывает спящее соединение. с del связь удаляется. Хотя я не вижу в этом смысла. Это ошибка pyodbc? - person TuanDT; 02.04.2019
comment
@TuanDT: del здесь не требуется. Он закрывает курсор, но обычно в этом нет необходимости, потому что то же самое происходит автоматически, когда csr выходит за пределы области видимости (а это обычно происходит достаточно скоро). Используйте его, только если вы хотите удалить csr из пространства имен и/или уменьшить количество ссылок на объект на единицу. Когда я впервые опубликовал это, я сохранил del, потому что это не обязательно неправильно, и я предположил, что ОП понял его цель. Но поскольку это, кажется, вызывает путаницу и не требуется для закрытия соединения, я удаляю его сейчас. - person unutbu; 02.04.2019
comment
Объекты pyodbc НЕ закрываются автоматически при использовании с менеджерами контекста!! Как указано в документации, на которую вы ссылаетесь, использование синтаксиса диспетчера контекста эквивалентно фиксации, НО НЕ закрытию соединения или курсора. См. github.com/mkleehammer/pyodbc/issues/43. - person johnDanger; 05.09.2019
comment
@johnDanger Под диспетчером контекста вы имеете в виду операторы with? - person NoName; 19.12.2019
comment
@NoName, да: PEP 343 - person johnDanger; 14.05.2020

Вы можете обернуть все соединение в менеджере контекста, как показано ниже:

from contextlib import contextmanager
import pyodbc
import sys

@contextmanager
def open_db_connection(connection_string, commit=False):
    connection = pyodbc.connect(connection_string)
    cursor = connection.cursor()
    try:
        yield cursor
    except pyodbc.DatabaseError as err:
        error, = err.args
        sys.stderr.write(error.message)
        cursor.execute("ROLLBACK")
        raise err
    else:
        if commit:
            cursor.execute("COMMIT")
        else:
            cursor.execute("ROLLBACK")
    finally:
        connection.close()

Затем сделайте что-то подобное там, где вам нужно подключение к базе данных:

with open_db_connection("...") as cursor:
    # Your code here

Соединение закроется, когда вы покинете блок with. Это также приведет к откату транзакции, если произойдет исключение или если вы не открыли блок с помощью with open_db_connection("...", commit=True).

person AndrewF    schedule 23.09.2010
comment
хорошая идея, но я использую Mysql и Sqlite .... не оракул (ну не напрямую :-)!) Где строка подключения к драйверу ... зачем импортировать sys? - person Merlin; 24.09.2010
comment
Ой, я думал, что заменил свой специфичный для оракула код на pyodbc, но я проглядел один (сейчас исправлено). Синтаксис тот же, так как оба используют общий API базы данных PEP 249. Я импортировал sys, чтобы я мог написать любые исключения для стандартной ошибки. Вы также можете использовать ведение журнала или просто оператор печати. И вы передаете ту же строку подключения, которую вы использовали раньше, в open_db_connection(). - person AndrewF; 24.09.2010
comment
Хорошо, глядя на код: что я получаю, делая? Кажется, много дополнительных строк кода, чтобы проверить, открыто ли соединение? - person Merlin; 24.09.2010
comment
Это именно то, что я искал, и он успешно работает с MySQLdb, но вам нужно изменить import contextlib на from contextlib import contextmanager или изменить декоратор на @contextlib.contextmanager. В любом случае, я думаю, но я предпочитаю первое. - person jedmao; 09.11.2011
comment
отличный метод, я искал такую ​​вещь. Но как поместить его в класс? Мой код на данный момент имеет соединение pyodbc как одно из полей класса: self.db = pyodbc.connect(path) теперь, когда я хочу перезаписать его вашим: self.db = open_db_connection("...") или что-то в этом роде, мое соединение закрывается, как только оно инициализируется pyodbc.ProgrammingError: The cursor's connection has been closed. - (и я хочу быть на связи во время выполнения. - person Intelligent-Infrastructure; 01.10.2013

Вы можете попробовать отключить пул, который включен по умолчанию. Дополнительные сведения см. в этом обсуждении.

import pyodbc
pyodbc.pooling = False
conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 

csr = conn.cursor()  
csr.close()
del csr
person Matthew Rankin    schedule 23.09.2010
comment
Забавно, я мог бы использовать пул, но mysql просто запускает еще одно соединение с новым идентификатором. - person Merlin; 24.09.2010

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

Чтобы закрыть соединение при вызове close(), вы должны установить для пула значение False:

import pyodbc

pyodbc.pooling = False
person hamzed    schedule 07.03.2017
comment
Когда вы говорите call close(), вы имеете в виду cursor.close()? Я предполагаю, что это так, поскольку connection.close() избежит необходимости устанавливать для пула значение false. - person Sean McCarthy; 15.08.2018

Вы можете определить класс БД, как показано ниже. Кроме того, как предложил andrewf, используйте диспетчер контекста для доступа к курсору. Я бы определил его как функцию-член. Таким образом, он сохраняет соединение открытым для нескольких транзакций из кода приложения и избавляет от ненужных повторных подключений к серверу.

import pyodbc

class MS_DB():
    """ Collection of helper methods to query the MS SQL Server database.
    """

    def __init__(self, username, password, host, port=1433, initial_db='dev_db'):
        self.username = username
        self._password = password
        self.host = host
        self.port = str(port)
        self.db = initial_db
        conn_str = 'DRIVER=DRIVER=ODBC Driver 13 for SQL Server;SERVER='+ \
                    self.host + ';PORT='+ self.port +';DATABASE='+ \
                    self.db +';UID='+ self.username +';PWD='+ \ 
                    self._password +';'
        print('Connected to DB:', conn_str)
        self._connection = pyodbc.connect(conn_str)        
        pyodbc.pooling = False

    def __repr__(self):
        return f"MS-SQLServer('{self.username}', <password hidden>, '{self.host}', '{self.port}', '{self.db}')"

    def __str__(self):
        return f"MS-SQLServer Module for STP on {self.host}"

    def __del__(self):
        self._connection.close()
        print("Connection closed.")

    @contextmanager
    def cursor(self, commit: bool = False):
        """
        A context manager style of using a DB cursor for database operations. 
        This function should be used for any database queries or operations that 
        need to be done. 

        :param commit:
        A boolean value that says whether to commit any database changes to the database. Defaults to False.
        :type commit: bool
        """
        cursor = self._connection.cursor()
        try:
            yield cursor
        except pyodbc.DatabaseError as err:
            print("DatabaseError {} ".format(err))
            cursor.rollback()
            raise err
        else:
            if commit:
                cursor.commit()
        finally:
            cursor.close()

ms_db = MS_DB(username='my_user', password='my_secret', host='hostname')
with ms_db.cursor() as cursor:
        cursor.execute("SELECT @@version;")
        print(cur.fetchall())
person Betran Jacob    schedule 15.11.2019

Наиболее распространенный способ обработки соединений, если язык не имеет самозакрывающейся конструкции, такой как Using в .NET, тогда вы должны использовать попытку -> finally, чтобы закрыть объекты. Возможно, у pyodbc есть какая-то форма автоматического закрытия, но вот код, который я делаю на всякий случай:

conn = cursor = None
try:
    conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 

    cursor = conn.cursor()  

    # ... do stuff ...

finally:
    try: cursor.close()
    except: pass
    try: conn.close()
    except: pass
person Timothy C. Quinn    schedule 23.04.2021