【发布时间】:2021-08-15 12:00:50
【问题描述】:
我正在尝试创建一种通过 locals() 之类的方式自动传递参数的方法,类似于 f 字符串的工作方式。
目前的工作方式
import pandas as pd
def my_func(conn, string_id, date, integer_ids):
sql = f"""
select * from RISK a
where STRING_ID = '{string_id}'
and DATE = {date}
and INTEGER_ID in ({','.join(map(str, integer_ids))})"""
df = pd.read_sql(sql, conn)
return df
但是,这种方法意味着我无法将 SQL 复制粘贴到 SQL developer 或类似软件中,然后从那里运行它。所以我想要一种使用参数的方法。
这似乎有两个问题
- 参数必须是字面量,所以不能传递列表
- 我需要手动创建一个字典,不能简单地传递
locals()之类的东西
我希望它的工作方式类似于下面的示例(显然行不通)
import pandas as pd
def my_func(conn, string_id, date, integer_ids):
sql = """
select * from RISK
where STRING_ID = :string_id
and DATE = :date
and INTEGER_ID in :integer_ids"""
df = pd.read_sql(sql, conn, params=locals())
return df
编辑:经过一番测试,也许我可以使用正则表达式来查找:param 的所有实例并将它们替换为参数值,例如
import re
pattern = '[\n\r].*:\s*([^\n\r]*)'
matches = re.findall(pattern,sql)
for match in matches:
sql = sql.replace(':'+match, eval(match))
它不是很漂亮,它引入了与对象类型相关的问题。例如。 string_id要封装成',日期也要转成字符串对象
最终编辑:感谢 perl,我现在有一个可行的解决方案来解决我的问题
def read_sql(sql, conn, params):
# Finds all words following a ":" sign in the sql
for p in re.findall(':(\w+)', sql):
if isinstance(params.get(p), (tuple, list)):
ext_params = {f'{p}_{i:03d}': p_i for i, p_i in enumerate(params.get(p))}
sql = sql.replace(f':{p}', f"(:{', :'.join(ext_params)})")
params.update(ext_params)
sql_text = sqlalchemy.text(sql)
return pd.read_sql(sql_text, conn, params=params)
def my_func(conn, string_id, date, integer_ids):
sql = """
select * from RISK
where STRING_ID = :string_id
and DATE = :date
and INTEGER_ID in :integer_ids"""
df = read_sql(sql, conn, locals())
return df
【问题讨论】:
-
会返回 df, sql workl?至少你会把带有参数的 sql 变成一个可以 print() 和剪切/粘贴的字符串?
-
@JonathanLeon 这需要我运行代码来获取可以复制粘贴的字符串,我需要一些无需运行代码即可工作的东西