SQLAlchemy强制在\()中使用文字

2024-10-06 07:58:39 发布

您现在位置:Python中文网/ 问答频道 /正文

有没有办法强制过滤器中的SQLAlchemyin_()操作符将literal值传递给数据库,而不是每个元素的单个绑定变量?你知道吗

我必须为一个IN子句执行一个(MySQL)查询,其中可能包含大量的值。(这里没有IN (SELECT ...)子查询选项)类似于:

query\
    .join(something)\
    .join(somethingelse)\
    .with_entities(somecol, someothercol)\
    .filter(somecol.in_(long_list_of_ids_in_a_list_variable))\
    .order_by(somecol)\
    .all()

为此,SQLAlchemy将以下内容发送到MySQL:

SELECT ... FROM ... JOIN ... 
WHERE id IN (%(id_1)s, %(id_2)s, %(id_3)s, %(id_4)s, %(id_5)s, %(id_6)s,
...
%(id_49)s, %(id_50)s)
ORDER BY ...
{'id_1': 718, 'id_2': 714, 'id_3': 713, 'id_4': 715, 'id_5': 712, 'id_6': 711,
...
'id_49': 657, 'id_50': 656}

随着IN项的数量越来越大,这个语句就会变得冗长和笨拙(例如,至少在调试期间)。(我真的不认为通过使用变量重用MySQL查询计划的任何潜在收益是显著的。)

我希望它将这些值作为内联文字发送:

WHERE id IN (718, 714, 713, 715, 712, 711,
...
657, 656)

有没有办法做到这一点(使用in_()或其他合适的构造)?请注意,我只希望将查询的IN段转换成这样的文字,而不是查询的所有其他部分。你知道吗

[我确实看了SQLAlchemy filter in_ operator,但没有从中得到适合我的答案。]


Tags: inid过滤器sqlalchemymysqlfilterwhereselect
2条回答

可以为IN子句使用^{}构造(注意sql注入风险):

from sqlalchemy_app import Base, Session, engine
import sqlalchemy as sa
from sqlalchemy.sql import text


class Something(Base):

    id = sa.Column(sa.Integer, primary_key=True)


class SomethingElse(Base):

    id = sa.Column(sa.Integer, primary_key=True)
    something_id = sa.Column(sa.Integer, sa.ForeignKey("something.id"))


if __name__ == "__main__":
    Base.metadata.drop_all(engine)
    Base.metadata.create_all(engine)
    s = Session()
    in_clause = text("something.id IN (1, 2, 3)")
    s.query(Something).join(SomethingElse).filter(in_clause).all()

发出此查询:

2019-08-10 14:23:32,329 INFO sqlalchemy.engine.base.Engine SELECT something.id AS something_id
FROM something INNER JOIN somethingelse ON something.id = somethingelse.something_id
WHERE something.id IN (1, 2, 3)

在1.4版本中(在编写这个即将发布的版本时),您可以使用扩展的bindparamliteral_execute=True来获得您想要的效果:

In [2]: class Foo(Model):
   ...:     id = Column(Integer, primary_key=True)
   ...:     data = Column(Integer)

In [3]: metadata.create_all()

In [4]: session.query(Foo).\
   ...:     filter(Foo.data.in_(
   ...:         bindparam('xs', range(10), expanding=True, literal_execute=True))).\
   ...:     all()
2019-09-07 20:35:04,560 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-09-07 20:35:04,561 INFO sqlalchemy.engine.base.Engine SELECT foo.id AS foo_id, foo.data AS foo_data 
FROM foo 
WHERE foo.data IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
2019-09-07 20:35:04,561 INFO sqlalchemy.engine.base.Engine ()
Out[4]: []

相关问题 更多 >