我有一个 SQL 语句,SELECT foo FROM bar WHERE id IN %s
。我有一个整数列表,例如[1, 2, 3]
我希望它能变成一个 SQL 语句,看起来像 SELECT foo FROM bar WHERE id IN (1, 2, 3)
.
我将 SQLAlchemy Core 用于其连接池,并使一些具有多个 VALUES
子句的插入更易于编写和维护。我更喜欢用原始 SQL 编写我的大部分查询。
要在 Pyscopg2 中执行此操作,我执行 cursor.execute('SELECT .. WHERE IN %s', (tuple(my_list),))
。但是,我无法在 SQLAlchemy 中完成这项工作。
engine.execute('SELECT ... WHERE IN %s', tuple(my_list))
引发异常:TypeError:并非所有参数都在字符串格式化期间转换。如果我只传递列表而不是包含在元组中,则会引发同样的异常。
如果我使用像 engine.execute('SELECT ... WHERE id IN :ids', ids=my_list)
这样的命名参数,我会得到一个 ProgrammingError
异常,因为 SQLAlchemy创建不正确的 SQL:SELECT * FROM foo WHERE id IN :ids
(它不会用 :ids 值替换我的变量)。如果我传递一个元组,也会引发同样的异常。
如何在 SQLAlchemy 中使用原始 SQL 来使用 WHERE IN()
子句?
最佳答案
这是一种仅由某些 DBAPI 支持的不寻常格式,因为它将项目的元组呈现为单独的 SQL 表达式,包括它呈现参数之间的逗号等,因此像 execute("select * from table where value in %s", (somelist, ))
这样的语句在数据库级别扩展为 select * from table where value in (1, 2, 3)
.
SQLAlchemy 不期望这种格式 - 它已经对传入参数进行了一些检查,因为它关注将参数路由到 DBAPI execute()
或 executemany()
方法,也接受一些不同的样式,这种转换的结果是这里的元组变平了。您可以通过再添加一个元组来让您的元组通过此解析:
from sqlalchemy import create_engine
engine = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
with engine.connect() as conn:
trans = conn.begin()
conn.execute("create table test (data integer)")
conn.execute(
"insert into test (data) values (%s)",
[(1, ), (2, ), (3, ), (4, ), (5, )]
)
result = conn.execute(
"select * from test where data in %s",
(
((1, 2, 3),),
)
)
print result.fetchall()
上述样式仅适用于某些 DBAPI。快速测试确认它适用于 psycopg2 和 MySQLdb,但不适用于 sqlite3。它与 DBAPI 用于将绑定(bind)参数发送到数据库的底层系统有更多关系; psycopg2 和 MySQLdb 都进行 Python 字符串插值和它们自己的转义,但是像 cx_oracle 这样的系统会将参数单独传递给 OCI,所以这种事情在那种情况下是行不通的。
SQLAlchemy 当然提供了 in_()使用 SQL 表达式构造时的运算符,但这不适用于直接字符串。
关于python - 使用 IN 子句替换 SQLAlchemy 原始 SQL 参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14512228/