我从StackOverflow上的一个有用的帖子中学到了如何在python (pyodbc)中调用SQL Server上的存储过程。在将我的代码修改为如下所示之后,我能够从我创建的db_engine
连接并运行execute()
。
import pyodbc
import sqlalchemy as sal
from sqlalchemy import create_engine
import pandas as pd
import urllib
params = urllib.parse.quote_plus(
'DRIVER={ODBC Driver 17 for SQL Server};'
f'SERVER=myserver.com;'
f'DATABASE=mydb;'
f'UID=foo;'
f'PWD=bar')
cobnnection_string = f'mssql+pyodbc:///?odbc_connect={params}'
db_engine = create_engine(connection_string)
db_engine.execute("EXEC [dbo].[appDoThis] 'MYDB';")
<sqlalchemy.engine.result.ResultProxy at 0x1121f55e0>
db_engine.execute("EXEC [dbo].[appDoThat];")
<sqlalchemy.engine.result.ResultProxy at 0x1121f5610>
然而,即使在Python中运行上述代码后没有返回任何错误,当我检查数据库时,我确认没有执行任何操作(更能说明问题的是,上述命令需要一到两秒钟才能完成,而在数据库管理工具上成功运行这些存储过程需要大约5分钟)。
我应该如何理解在上述设置中不正常工作,以便正确调试?我通过数据库管理工具运行完全相同的代码,没有出现任何问题——存储过程按预期执行。有什么可以阻止这种情况通过Python发生呢?执行的SQL是否需要提交?是否有一种方法来调试使用返回的ResultProxy
?如有任何建议,我将不胜感激。
直接在Engine
对象上调用.execute()
是一种过时的使用模式,并且会从SQLAlchemy version 1.4开始发出弃用警告。目前,首选的方法是使用使用engine.begin()
:
with
块)。import sqlalchemy as sa
# …
with engine.begin() as conn: # transaction starts here
conn.execute(sa.text("EXEC [dbo].[appDoThis] 'MYDB';"))
# On exiting the `with` block the transaction will automatically be committed
# if no errors have occurred. If an error has occurred the transaction will
# automatically be rolled back.
指出:
- 当传递SQL命令字符串时,它应该包装在SQLAlchemy
text()
对象中。 - SQL Server存储过程(和匿名代码块)在绝大多数情况下应该以
SET NOCOUNT ON;
开头。如果不这样做,可能会导致合法的结果或错误被"卡在后面"。INSERT
、UPDATE
或DELETE
等DML语句可能发出的任何行计数。