如何使用read_sql_query()调用带参数的SQL存储过程?
如何使用read_sql_query()调用带参数的SQL存储过程?
看起来你在调用带参数的SQL Server存储过程时遇到了几个常见问题,我来帮你梳理一下解决方案:
先说说你遇到的两个错误原因
第一个错误:
TypeError: unsupported operand type(s) for &: 'str' and 'int'
你用了"EXECUTE dbo.LibraryBookData @BookId = " & bookId来拼接字符串和数字,但&是VB/Visual Basic里的字符串拼接运算符,Python里对应的是+。不过更重要的是:绝对不要直接把参数拼接到SQL语句里,这会带来严重的SQL注入风险,而且当参数是字符串类型时还会引发语法错误。第二个错误:
TypeError: Connection.execute() got an unexpected keyword argument 'bookId'
SQLAlchemy的execute()方法并不支持直接传命名关键字参数(比如BookId=bookId),正确的传参方式是传递一个参数字典,或者位置参数。而且你原本的代码里同时用了con.execute()和pd.read_sql_query(),其实后者可以直接完成查询和结果转换,不需要单独调用execute()。
正确的实现方式
这里提供两种靠谱的写法,都能安全传递参数并获取存储过程的结果:
方法1:直接用pd.read_sql_query()配合参数
这是最简洁的方式,利用read_sql_query的params参数传递参数字典:
import pandas as pd from sqlalchemy import create_engine, text, URL bookId = 653 # 构建连接信息 connection_string = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=dev;DATABASE=Nemisis" connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string}) engine = create_engine(connection_url) # 定义带命名占位符的存储过程调用语句 qry = text("EXECUTE dbo.LibraryBookData @BookId = :BookId") # 直接用read_sql_query执行并转换为DataFrame,通过params传递参数 df = pd.read_sql_query(qry, engine, params={"BookId": bookId})
方法2:手动管理连接执行后再转DataFrame
如果你需要手动控制连接生命周期,可以这样写:
import pandas as pd from sqlalchemy import create_engine, text, URL bookId = 653 connection_string = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=dev;DATABASE=Nemisis" connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string}) engine = create_engine(connection_url) qry = text("EXECUTE dbo.LibraryBookData @BookId = :BookId") with engine.connect() as con: # 传递参数字典给execute方法 rs = con.execute(qry, {"BookId": bookId}) # 将结果转换为DataFrame df = pd.DataFrame(rs.fetchall(), columns=rs.keys())
关键要点总结
- 杜绝直接拼接参数:始终使用参数绑定的方式传递变量,既安全又能避免语法错误。
- SQLAlchemy占位符:用
:参数名作为命名占位符,比位置占位符(?)更清晰,尤其是在多个参数的场景下。 - read_sql_query的params参数:这是pandas专门用来传递查询参数的入口,使用起来非常方便。
备注:内容来源于stack exchange,提问作者SkyeBoniwell
相关产品推荐
相关产品推荐

