pyodbc执行SQL Server存储过程无报错但每次运行结果不一致
问题根因
出现这个问题的核心是pyodbc默认未完整消费存储过程返回的所有结果流,就提前触发事务提交,导致存储过程的循环插入逻辑被随机中断:
- 你在SSMS中执行存储过程时,SSMS会同步等待整个存储过程的所有逻辑执行完毕、所有返回结果全部接收完成后才结束操作,因此循环会完整跑完所有插入步骤,每次生成的表数据完全一致。
- 你的存储过程循环中使用
SELECT @dateFrom = DATEADD(day, 1,@dateFrom)做变量赋值,这个写法默认会向客户端返回每次赋值的行计数结果(也就是"DONEINPROC"消息)。pyodbc的execute()方法在拿到第一个返回结果后就会认为语句执行完成,如果你此时直接调用commit(),SQL Server会直接终止该存储过程的后续执行。每次循环执行到哪一步被终止完全是随机的,因此最终查询到的max_date_每次都不一样。
修复方案
二选一即可解决问题:
- 修改存储过程,关闭隐式结果返回:在存储过程最开头加上
SET NOCOUNT ON;,禁止执行过程中返回行计数类的隐式消息,pyodbc就会等待整个存储过程执行完毕后才返回。修改后的存储过程开头如下:CREATE PROCEDURE procedure_create_tbl_date_range AS SET NOCOUNT ON; DROP TABLE IF EXISTS tbl_date_range; -- 后续原有逻辑保持不变 - 修改Python代码,主动消费完所有结果集:如果不想改存储过程,可以在执行存储过程后,循环调用
nextset()拉取完所有返回结果,再执行提交。修改后的Python代码逻辑如下:def run_procedure(my_procedure): conn = pyodbc.connect('Driver={SQL Server};' 'Server=.\SQLEXPRESS;' 'Database=my_db;' 'Trusted_Connection=yes' ) cur = conn.cursor() try: cur.execute(my_procedure) # 拉取所有剩余结果集,确保存储过程执行完成 while cur.nextset(): pass conn.commit() except Exception as e: conn.rollback() print(e) finally: cur.close() conn.close()
补充说明:你当前的存储过程循环逻辑会跳过初始的
@dateFrom值(2021-12-12),最终生成的日期范围是2021-12-13到2022-12-12,如果需要包含起始日期可以调整赋值和插入的顺序,这个逻辑问题和本次的随机结果故障无关。
内容的提问来源于stack exchange,提问作者Amir Py
相关产品推荐
相关产品推荐

