如何正确使用pd.read_sql带参数查询SQL Server?
问题原因与解决方案
这个问题其实是对参数化查询的作用范围理解有误——参数化查询里的params参数是用来传递数据值的(比如WHERE id = ?里的id值),而不是用来替换SQL中的标识符(列名、表名、函数名这类数据库对象名称)的。
为什么会得到重复的字段名?
当你执行query = 'SELECT ?,? FROM position_names'并传入params = ['position_id','position_name']时,SQL Server会把这两个参数当作字符串常量来处理,相当于实际执行的SQL是:
SELECT 'position_id', 'position_name' FROM position_names
这就意味着,表的每一行都会返回'position_id'和'position_name'这两个固定字符串,所以你看到的结果全是重复的字段名,而不是列对应的数据。
正确的动态列名查询方式
如果需要动态指定查询的列,你需要安全地拼接SQL语句,但直接拼接字符串有SQL注入风险,所以一定要先验证传入的列名是否是目标表中存在的合法列:
- 先获取目标表的合法列名列表:
# 查询position_names表的所有合法列 valid_columns = pd.read_sql( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'position_names'", engine )['COLUMN_NAME'].tolist()
- 验证你要查询的列是否合法,再拼接SQL:
target_columns = ['position_id', 'position_name'] # 检查所有目标列都在合法列表中 if all(col in valid_columns for col in target_columns): # 拼接列名到SQL中 query = f"SELECT {', '.join(target_columns)} FROM position_names" # 执行查询 result = pd.read_sql(query, engine) else: raise ValueError("传入的列名包含非法字段,可能存在SQL注入风险")
这种方式既满足了动态选择列的需求,又通过列名验证避免了SQL注入的安全问题。
内容的提问来源于stack exchange,提问作者Ethan
相关产品推荐
相关产品推荐

