
在 SQLAlchemy 中,connection.execute() 不接受裸元组作为参数,必须使用命名字典(如 {"col1": val1, "col2": val2})或符合规范的参数结构;直接传入 ('a', 1, 2) 会触发 “List argument must consist only of tuples or dictionaries” 错误。
在 sqlalchemy 中,`connection.execute()` 不接受裸元组作为参数,必须使用命名字典(如 `{"col1": val1, "col2": val2}`)或符合规范的参数结构;直接传入 `('a', 1, 2)` 会触发 “list argument must consist only of tuples or dictionaries” 错误。
SQLAlchemy 的 text() 执行机制要求参数以命名绑定(named binding)形式传递,即使用 :param_name 占位符配合字典参数。你原代码中传入的元组 ('2025-01-31', 12, 22) 虽然逻辑清晰,但不符合 SQLAlchemy 的参数协议——它既不是由字典组成的列表(用于批量执行),也不是单个字典,更不是支持位置占位符(如 ?)的底层驱动原生模式(SQLAlchemy 默认禁用位置参数,除非显式启用 literal_binds 或使用特定方言配置)。
✅ 正确做法:使用命名参数字典 + 冒号占位符
这是 SQLAlchemy 官方推荐、安全且可移植的方式:
from sqlalchemy import text
def execute_query(sql_query, engine, params_dict=None):
# 确保 params_dict 是字典(None 时设为空字典)
params_dict = params_dict or {}
with engine.connect() as connection:
result = connection.execute(text(sql_query), params_dict)
connection.commit()
return result
# 调用示例:
sql = "INSERT INTO x.aggregation_dates (position_date, account_id, horizon) VALUES (:position_date, :account_id, :horizon)"
params = {
"position_date": "2025-01-31",
"account_id": 12,
"horizon": 22
}
execute_query(sql, engine, params)⚠️ 注意事项:
- 占位符名称(如
:position_date)必须与字典键完全一致(区分大小写); - 不要混用
?(SQLite 风格)或%s(PostgreSQL/MySQL 风格)——SQLAlchemy 的text()默认仅解析:name命名绑定; - 若需执行批量插入,应传入字典列表:
[{"a":1,"b":2}, {"a":3,"b":4}],此时execute()会自动批处理; - 切勿使用 f-string 或
.format()拼接用户输入(如f"VALUES {params_tuple}"),这将导致严重 SQL 注入漏洞,即使在内部测试中也应杜绝。
? 补充:若坚持使用位置参数(不推荐)
极少数场景下需兼容旧脚本,可通过 engine.raw_connection() 绕过 SQLAlchemy 参数层(失去 ORM 安全保障),但强烈建议重构为命名参数。例如:
# ❌ 反面示例(禁止用于生产/用户输入)
sql_raw = f"INSERT INTO x.aggregation_dates VALUES {params_tuple}"
connection.execute(text(sql_raw)) # 危险!无转义、无类型检查总结:始终优先使用 :name 命名占位符 + 字典参数,它兼顾安全性、可读性与跨数据库兼容性。这是 SQLAlchemy 设计哲学的核心实践,也是避免“参数类型错误”的根本解法。

















