20

我有一条 SQL 语句,SELECT foo FROM bar WHERE id IN %s. 我有一个整数列表,例如[1, 2, 3],我希望它变成一个看起来像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 值代替我的变量)。如果我传递一个元组,也会引发同样的异常。

如何WHERE IN()在 SQLAlchemy 中使用原始 SQL 的子句?

4

2 回答 2

16

这是一种仅由某些 DBAPI 支持的不寻常格式,因为它将项目元组呈现为单独的 SQL 表达式,包括在参数之间呈现逗号等,因此像这样的语句execute("select * from table where value in %s", (somelist, ))在数据库级别扩展为select * from table where value in (1, 2, 3).

SQLAlchemy 并不期待这种格式 - 它已经对传入参数进行了一些检查,因为它涉及将参数路由到 DBAPIexecute()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 用来向数据库发送绑定参数的底层系统有关;psycopg2 和 MySQLdb 都做 Python 字符串插值和它们自己的转义,但是像 cx_oracle 这样的系统会将参数单独传递给 OCI,所以这种事情在那种情况下是行不通的。

SQLAlchemy 在使用 SQL 表达式结构时当然提供了in_()运算符,但这不适用于直接字符串。

于 2013-01-24T23:17:13.883 回答
15

我使用 SQLAlchemy 0.9.8、python 2.7、MySQL 5.X 和 MySQL-Python 作为连接器,在这种情况下,需要一个元组。我的代码如下:

id_list = [1, 2, 3, 4, 5] # in most case we have an integer list or set
s = text('SELECT id, content FROM myTable WHERE id IN :id_list')
conn = engine.connect() # get a mysql connection
rs = conn.execute(s, id_list=tuple(id_list)).fetchall()

希望一切都适合你。

于 2015-04-08T04:27:39.300 回答