如何在sqlalchemy范围的会话中执行MySQL存储过程以返回单个结果数据集(对于flask web应用程序)



我发现的大多数调用MySQL存储过程并在python中存储结果的示例都使用带有cursorcallproc方法,但使用sqlalchemy创建的scoped_session对象上不存在callproc方法。我使用scoped_session是因为我正在构建一个flask应用程序,该应用程序将在应用程序的不同部分使用会话(作用域会话适用于简单的select语句(。存储过程进行一次选择并返回数据。我使用mysql.connector作为mysql驱动程序。

我尝试了以下烧瓶内路线,但我遇到了一个错误:

mysql_engine = create_engine(conn_string)
DbSession = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=mysql_engine))
@app.route('/')
def index():
# register session
DbSession()
sql = 'call myStoredProc(:param);'
# call stored procedure: getting error "Use multi=True when executing multiple statements"
result = DbSession.execute(sql, {'param': 'param value'})
data = [dict(r) for r in result]
# remove session from register
DbSession.remove()
# pass data to template to render
return render_template('index.html', data = data)

如代码所示,我得到了以下错误:;执行多个语句时使用multi=True"我已经了解到,mysql.connector为存储过程假设了一个out参数,因此默认情况下,即使存储过程只是运行一个select查询,它也会认为它是一个多语句。DbSession上的execute方法不接受选项。

错误中的建议是对多个语句使用cmd_query_iter,但这在DbSession对象上也不存在。

有什么建议吗?

据我所知,SQLAlchemy不支持直接调用存储过程。文档建议使用原始DB-API连接的callproc方法。

可以从发动机访问连接;它也可以通过会话访问,但这仍然是通过引擎进行的。

下面的示例代码显示了这两种方法。请注意,访问调用过程结果的方法可能会因参数和使用的连接器而异——有关一些示例,请参阅此答案。

import mysql.connector
import sqlalchemy as sa
from sqlalchemy import orm
# Setup the database
DATA = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
DDL1 = """
CREATE TABLE IF NOT EXISTS test_table (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
a INT,
b INT,
c INT)
"""
DDL2 = """
CREATE PROCEDURE IF NOT EXISTS test_procedure (IN p1 INT)
BEGIN
SELECT a, b, c FROM test_table
WHERE a > p1;
END
"""
DML1 = """DELETE FROM test_table"""
DML2 = """INSERT INTO test_table (a, b, c) VALUES (%s, %s, %s)"""
CALL1 = """CALL test_procedure(:param)"""
conn = mysql.connector.connect(database='test')
cur = conn.cursor()
cur.execute(DDL1)
cur.execute(DDL2)
cur.execute(DML1)
for row in DATA:
cur.execute(DML2, row)
conn.commit()
conn.close()

# Call the procedure
engine = sa.create_engine('mysql+mysqlconnector:///test')
Session = orm.scoped_session(orm.sessionmaker(autocommit=False, autoflush=False, bind=engine))
session = Session()
raw_conn = session.connection().engine.raw_connection()
cur = raw_conn.cursor()
cur.callproc('test_procedure', [1])
print('Using session')
for result in cur.stored_results():
print(result.fetchall())
Session.remove()
print('Using engine directly')
raw_conn = engine.raw_connection()
cur = raw_conn.cursor()
cur.callproc('test_procedure', [1])
for result in cur.stored_results():
print(result.fetchall())

最后一个观察结果是:在我看来,通过会话访问的原始连接不在会话的事务上下文中,因此根据事务隔离设置,使用会话所做的更改可能对使用连接所做的修改不可见。

最新更新