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
    
python
database-connection
Merlin
Merlin
发布于 2010-09-24
6 个回答
unutbu
unutbu
发布于 2021-04-24
已采纳
0 人赞同

连接有一个close方法,如PEP-249 (Python Database API Specification 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

Since the pyodbc connectioncursor都是上下文管理器,现在把它写成这样会更方便(也更合适)。

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!! 

See https://github.com/mkleehammer/pyodbc/issues/43来解释为什么不调用conn.close()。

注意,与原始代码不同,这将导致conn.commit()被调用。使用外层的with语句来控制你希望commit被调用的时间。

还要注意,无论你是否使用with语句,每the docs,

当连接被删除时(通常是当它们超出范围时)会自动关闭,所以你通常不需要调用conn.close(),但如果你愿意,你可以显式关闭连接。

和类似的for cursors(我的强调)。

游标在被删除后会自动关闭(通常是当它们超出范围时)。所以通常不需要调用csr.close().

这是最佳做法吗?手动关闭光标,然后删除它,再关闭连接?
我也想知道@DustinMichels问题的答案。
Per the docs, "当连接被删除时(通常是当它们超出范围时)会自动关闭,所以你通常不需要调用这个,但如果你愿意,你可以显式地关闭连接"。还有对光标也是如此 too.
@TuanDT:这里不需要del。它确实关闭了游标,但通常没有必要,因为当csr超出范围时,同样的事情会自动发生(而且通常很快就会发生)。 只有当你想把csr从命名空间中移除和/或减少对该对象的引用时才使用它。当我第一次发这个帖子时,我保留了del,因为它不一定是错的,而且我认为OP理解它的目的。但由于它似乎造成了混乱,而且对于关闭连接来说也不是必需的,所以我现在把它删除了。
当使用上下文管理器时,pyodbc 对象不会自动关闭!!。正如你所链接的文档所指出的,使用上下文管理器的语法等同于提交但不关闭连接或游标。参见github.com/mkleehammer/pyodbc/issues/43
AndrewF
AndrewF
发布于 2021-04-24
0 人赞同

你可以将整个连接包裹在一个上下文管理器中,如下所示。

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()
        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)打开块,这也将回滚交易。

好主意,但我用的是Mysql和Sqlite....,而不是oracle(好吧,不是直接的:-)! 驱动程序的连接字符串在哪里...为什么要导入sys?
哎呀,我以为我用pyodbc替换了我的oracle专用代码,但我忽略了一个问题(现在修好了)。语法是一样的,因为两者都使用通用的PEP 249数据库API。我导入了sys,所以我可以把任何异常写到标准错误中。你也可以使用日志或只是一个普通的打印语句。你把你之前使用的连接字符串传递给open_db_connection()。
好了,看了一下代码。我这样做能得到什么?似乎有很多额外的代码行来检查连接是否已打开?
这正是我在寻找的,而且我已经成功地与MySQLdb一起工作了,但你必须把import contextlib改为from contextlib import contextmanager或把你的装饰器改为@contextlib.contextmanager。我想,两种方式都可以,但我更喜欢前者。
很好的方法,我一直在寻找这样一个东西。但是我怎样才能把它放在一个类里面呢?我的代码中,pyodbc连接是类中的一个字段。替换代码0】现在当我想把它覆盖到你的。替换代码1】或者任何这样的方式,我的连接在初始化后就被关闭了 pyodbc.ProgrammingError: The cursor's connection has been closed.。- 而我想在整个运行过程中保持连接。
Matthew Rankin
Matthew Rankin
发布于 2021-04-24
0 人赞同

你可以尝试关闭池子,它在默认情况下是启用的。见this讨论更多信息。

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
    
有趣的是,我可以使用池子,但mysql只是用一个新的ID启动另一个conn。
Betran Jacob
Betran Jacob
发布于 2021-04-24
0 人赞同

你可以像下面这样定义一个DB类。另外,如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()
            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())
    
Tomy
I think best solution is in this link https://stackoverflow.com/a/38078544/12274302
hamzed
hamzed
发布于 2021-04-24
0 人赞同

根据 pyodbc 文档,与 SQL 服务器的连接默认情况下不会关闭。一些数据库驱动程序在调用close()时不关闭连接,以节省到服务器的往返次数。

当你打电话时,要关闭你的连接close()你应该把池子设置为False。

import pyodbc
pyodbc.pooling = False
    
当你说 "调用close()",你是指cursor.close()吗?我想是的,因为connection.close()可以避免将池子设置为假。
Timothy C. Quinn
Timothy C. Quinn
发布于 2021-04-24
0 人赞同

The most common way to handle connections, if the language does not have a self closing construct like Using in .NET, then you should use a try -> finally to close the objects. Its possible that pyodbc does have some form of automatic closing but here is the code I do just in case:

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