【问题标题】:Why was my entry to my SQL Server table committed when I did not call conn.commit()?为什么当我没有调用 conn.commit() 时,我对 SQL Server 表的条目被提交了?
【发布时间】:2019-09-19 15:01:30
【问题描述】:

我很好奇为什么我的数据会被提交到服务器,因为据我了解,当通过pyodbc 将行写入 SQL Server 时,您必须先调用 connection.commit(),然后才能将数据提交到服务器。但是,当我在不使用 commit() 的情况下运行以下代码时,数据仍被提交到我的表中,经验证:

cursor.execute("SELECT * FROM db.table")
tables = cursor.fetchall()

以及通过 SSMS 手动检查表格。

这是我用来更新表并认为它不会提交数据但确实提交的代码示例。

import pyodbc


row_string = 'FIRST_NAME, LAST_NAME, MEMBER_EMAIL, SUPERVISOR_NAME, SUPERVISOR_EMAIL, MEMBER_TITLE'
value_string = [['Name', 'LName', 'Name.LName@randome.com', 'Super Name',
                'Super.Name@random.com', 'Some Title']]
ex_value = "insert into DB.TABLE ({}) values (?, ?, ?, ?, ?, ?)".format(row_string)
print(ex_value)

odbc_driver, server, db = '{ODBC Driver 17 for SQL Server}', 'server_address', 'dbname'

# I did try to use this try/except statement provided by Chiheb Nexus below.
# Oddly enough this committed the data but my print statement did not execute.

with pyodbc.connect(driver=odbc_driver, host=server, database=db, trusted_connection='yes') as conn:
    try:
        conn.autocommit = False
        cursor = conn.cursor()
        cursor.executemany(ex_value, value_string)
        cursor.execute("SELECT * FROM db.TABLE")
        tables = cursor.fetchall()

        for row in tables:
            print('Row: {}'.format(row))

    except pyodbc.DatabaseError as err:
        conn.rollback()
    else:
        conn.commit()
    finally:
        conn.autocommit = True

    cursor.close()

数据结果:

insert into db.TABLE (FIRST_NAME, LAST_NAME, MEMBER_EMAIL, SUPERVISOR_NAME, SUPERVISOR_EMAIL, MEMBER_TITLE) values (?, ?, ?, ?, ?, ?)
Row: (9002, 'Name', 'LName', 'Name.LName@randome.com', 'Super Name', 'Super.Name@random.com', 'Some Title', 'None')
Row: (9001, 'Name1 ', 'LName1', 'Name1.LName1@random.com', 'Super Name', 'Super.Name@random.com', 'Some Title', 'None')

如果有人知道(不是奇怪的问题)为什么我的数据以相反的顺序表示,那么附带问题。我希望我的数据按照记录 9001 而不是 9002 从上到下排序,而不是像上面显示的相反。

【问题讨论】:

    标签: python sql-server odbc pyodbc


    【解决方案1】:

    就像Pyodbc documentationexecutemany 函数的情况所说的那样:

    另外,如果 autocommit 为 True,请小心。在这种情况下,提供的 SQL 语句将被提交给每个记录 参数序列。因此,如果在处理过程中发生错误, 您最终将在数据库中提交一些记录,并且 其余的没有,而且可能不容易分辨哪些记录有 被承诺。因此,您可能需要考虑将自动提交设置为 错误(并明确提交()/回滚())以确保所有 记录提交到数据库或不提交

    因此,您需要在cursor.executemany 之前添加conn.autocommit = False 并明确添加conn.commit()conn.rollback(),否则您的更改将提交到您的数据库。

    这是文档中的一个示例:

    try:
        cnxn.autocommit = False
        params = [ ('A', 1), ('B', 2) ]
        cursor.executemany("insert into t(name, id) values (?, ?)", params)
    except pyodbc.DatabaseError as err:
        cnxn.rollback()
    else:
        cnxn.commit()
    finally:
        cnxn.autocommit = True
    

    【讨论】:

    • 啊。我认为 autocommit = True 仅适用于 SSMS 而不是 ODBC 连接。我会对此进行测试,但我确信这是问题所在。
    • @Mike-SMT 如果有问题,请随时通知我。此外,还有一个示例说明如何更改文档中的 autocommit
    • 我在cursor.executemany()之前添加了conn.autocommit = False,数据仍然提交。
    • @Mike-SMT 我明白了。您是否尝试过文档的给定示例并明确添加 commitrollback
    • 哼。如果我告诉 autocommit = False,为什么我必须告诉它回滚?这很有趣。
    【解决方案2】:

    如果你需要自己控制事务,你需要同时拥有开始事务和提交/回滚。否则,不处于显式事务模式,SQL Server 将每个命令作为一个事务提交。

    【讨论】:

    • 你能举个例子吗?
    • 这是从 SQL Server 的角度来看的。在 python 中,set autocommit = False 会启动一个显式的 tran,相当于 BEGIN TRANSACTION。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-08
    • 1970-01-01
    相关资源
    最近更新 更多