Python操作MSSQL时同一列表两次作为IN参数传参报错解决
问题根因
pyodbc的参数化查询严格按?占位符的顺序一一匹配入参,你写的SQL里两个IN子句各生成了4个占位符,总计8个,但传入的参数只有4个,必然触发参数数量不匹配的报错。
另外原代码存在几处语法笔误,需要先修正才能正常运行:
create_engine的连接字符串未包裹字符串引号- 定义
placeholder时多写了一个赋值等号 query多行字符串未闭合三引号pd.read_sql_query传入的SQL对象、参数名和前面定义的变量不匹配
解决方法
方法1:重复传入参数列表(最简单,适配短列表场景)
每个?占位符都需要对应一个独立入参,你只需要在传参时把原列表重复拼接一次,让传入的参数总数和占位符数量一致即可。元组/列表乘以2就可以快速实现元素重复,不需要手动写两遍列表。
修正后的可运行代码如下:
import pandas as pd import datetime from datetime import date from sqlalchemy import create_engine # 修正:连接字符串加引号 cnxn = create_engine("mssql+pyodbc://<connection_string>?driver=SQL+Server") end_date = date.today().strftime('%Y-%m-%d') start_date = (date.today() - datetime.timedelta(days=90)).strftime('%Y-%m-%d') temp_list = ('item1', 'item2', 'item3', 'item4') # 修正:去掉多余的等号 placeholder = ','.join('?' for i in range(len(temp_list))) # 修正:闭合三引号,同时把日期也改为参数化,避免SQL注入风险 query = f""" SELECT something FROM Table where date between ? and ? and name IN ({placeholder}) UNION SELECT something FROM other_table where date between ? and ? and name IN ({placeholder}) """ df = pd.read_sql_query( query, cnxn, # 按占位符顺序传参:2个日期参数 + 第一组IN参数 + 2个日期参数 + 第二组IN参数 params=(start_date, end_date, *temp_list, start_date, end_date, *temp_list) ) # 如果暂时不想改日期参数化,保持原f-string拼接日期的写法,params直接传 temp_list*2 即可 writer = pd.ExcelWriter('foo.xlsx', datetime_format='hh:mm:ss') df.to_excel(writer, sheet_name='bar', index=False) writer.close() # 新版本pandas推荐用close(),旧版本save()也可兼容
方法2:用CTE/临时表预存列表值(适配长列表场景)
如果你的IN列表元素很多,重复传参会让SQL语句变得冗余,这时候可以先通过CTE或者临时表把列表值提前存入,后面两个查询都直接关联这组值做过滤,只需要传一次列表参数即可。核心逻辑示例:
WITH item_filter(name) AS ( SELECT ? UNION ALL SELECT ? UNION ALL SELECT ? UNION ALL SELECT ? ) SELECT something FROM Table t INNER JOIN item_filter f ON t.name = f.name where date between ? and ? UNION SELECT something FROM other_table ot INNER JOIN item_filter f ON ot.name = f.name where date between ? and ?
这种写法下你只需要传一次列表参数+两个日期参数即可,适合列表元素超过20个的场景。
注意:不要为了省事直接把列表值拼接进SQL字符串,会带来SQL注入风险,参数化查询是最安全的做法。
内容的提问来源于stack exchange,提问作者iNeedScissors61
相关产品推荐
相关产品推荐

