将列表绑定到 Pandas read_sql_query 中的参数与其他参数
Posted
技术标签:
【中文标题】将列表绑定到 Pandas read_sql_query 中的参数与其他参数【英文标题】:Binding list to params in Pandas read_sql_query with other params 【发布时间】:2016-08-18 20:04:02 【问题描述】:我一直在尝试测试使我的代码运行的各种方法。首先,我有这个清单:
member_list = [111,222,333,444,555,...]
我试图将它传递给这个查询:
query = pd.read_sql_query(
"""
select member id
,yearmonth
from queried_table
where yearmonth between ? and ?
and member_id in ?
""", db2conn, params = [201601, 201603, member_list])
但是,我收到一条错误消息:
'无效的参数类型。 param-index=2 param-type=list', 'HY105'
所以我环顾四周并尝试使用格式化字符串:
query = pd.read_sql_query(
"""
select member id
,yearmonth
from queried_table
where yearmonth between ? and ?
and member_id in (%s)
""" % ','.join(['?']*len(member_list), db2conn, params = [201601, 201603, tuple(member_list)])
现在,我得到了错误:
'SQL 包含 18622 个参数标记,但提供了 3 个参数', 'HY000'
因为它要填充格式化字符串中的所有?
占位符。
那么,最终,有没有办法以某种方式评估列表并传递每个单独的元素以绑定到?
,或者我可以使用另一种方法来让它工作?
顺便说一句,我使用pyodbc
作为我的连接器。
提前致谢!
【问题讨论】:
【参考方案1】:将其分为三个部分,以帮助隔离问题并提高可读性:
-
构建 SQL 字符串
设置参数值
执行pandas.read_sql_query
构建 SQL
首先确保?
占位符设置正确。使用str.format 与str.join 和len 根据member_list
长度动态填写?
s。下面的示例假设有 3 个 member_list
元素。
示例
member_list = (1,2,3)
sql = """select member_id, yearmonth
from queried_table
where yearmonth between 0 and 0
and member_id in (1)"""
sql = sql.format('?', ','.join('?' * len(member_list)))
print(sql)
返回
select member_id, yearmonth
from queried_table
where yearmonth between ? and ?
and member_id in (?,?,?)
设置参数值
现在确保参数值被组织成一个 flat 元组
示例
# generator to flatten values of irregular nested sequences,
# modified from answers http://***.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python
def flatten(l):
for el in l:
try:
yield from flatten(el)
except TypeError:
yield el
params = tuple(flatten((201601, 201603, member_list)))
print(params)
返回
(201601, 201603, 1, 2, 3)
执行
最后将sql
和params
值放在read_sql_query
调用中
query = pd.read_sql_query(sql, db2conn, params)
【讨论】:
扁平化功能做到了!!!但是,我想我只是遇到了参数上限的问题。显然,我的成员列表有 84000 多个成员,但是当我尝试为所有这些成员动态填写?
占位符时,Python 限制为最多 18,622 个。看起来我将不得不拆分我的成员列表来执行此操作。感谢您的帮助!
乐于助人。由于您正在使用那么多 member_list
值,因此可能会通过填充另一个表然后内连接来过滤结果来获得更好的性能(并修复参数限制)。 pandas.DataFrame.to_sql 可能有助于创建表。
对于pymysql
,你需要一个%s
占位符,所以它应该是','.join(['%s'] * len(member_list))
Bryan 的回答太棒了!不过需要注意的是,默认情况下,params 不是 pd.read_sql_query 中的第三个参数,因此它仅在我指定 params=params 时才有效:pd.read_sql_query(sql, db2conn, params=params) 更多:pandas.pydata.org/pandas-docs/stable/reference/api/…跨度>
【参考方案2】:
如果您使用的是 python 3.6+,您还可以使用格式化字符串litteral 进行查询(参见https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498)
start, end = 201601, 201603
selected_members = (111, 222, 333, 444, 555) # requires to be a tuple
query = f"""
SELECT member_id, yearmonth FROM queried_table
WHERE yearmonth BETWEEN start AND end
AND member_id IN selected_members
"""
df = pd.read_sql_query(query, db2conn)
【讨论】:
注意:“开始”和“结束”可能需要转换为字符串,具体取决于数据库表中“年月”列的类型... 虽然您提出的方法可行,但不推荐使用,因为它容易受到 SQL 注入攻击。 确实有一点风险(但是很多事情无论如何都会使查询失败)。话虽如此,提议的解决方案并没有提供更多的安全性(只是检查过去的列表是否可以通过使用连接来枚举)。我认为如果你走得足够远,可能也容易受到注入攻击......无论如何,首先我不清楚该查询是否服务于前端直接;其次,验证输入的责任应该在包装该查询的函数中,而不是查询字符串本身... 最后一件事,根据所使用的数据库引擎,当selected_members
元组由单个元素组成时,您可能需要对其进行验证。这是因为在 python 中它会打印为(111,)
,而(至少通过 psycopg2)查询只有在(111)
时才会成功解析。【参考方案3】:
query = 'Select count(*) cnt from TBL_DESK_AUDIT where trunc(DATETIMECREATED) = trunc(sysdate) and DESK_NAME =' + "'"+dataframe_list1[0][0] + "'"
print(query)
df_TBL_DESK_AUDIT = pd.read_sql_query(query, connect);
【讨论】:
以上是关于将列表绑定到 Pandas read_sql_query 中的参数与其他参数的主要内容,如果未能解决你的问题,请参考以下文章