SQLAlchemy 过滤多个列

135

我如何将两列合并并应用筛选条件?例如,我想同时在“firstname”和“lastname”这两列中搜索。以下是我仅搜索一列的做法:

query = meta.Session.query(User).filter(User.firstname.like(searchVar))

5
我认为这个问题与我遇到的问题相符,但是答案并不适用于我的特定情况。如果名字是“joe”,姓氏是“smith”,我正在寻找一个过滤语句,当提供的searchVar是“joe smith”时匹配。也就是说,在进行测试之前,需要将字段连接起来(添加空格)。看起来像是一个非常现实的场景。 - Groovee60
2
@Groovee60 这正是我正在寻找的。如果您找到了解决方案,能否分享一下?非常感谢。 - Lilylakshi
任何寻找@Groovee60在7年前提出的问题解决方案的人现在都可以使用映射属性 - Jeremy
5个回答

181

有多种方法可以做到这一点:

使用filter()函数(运算符)

query = meta.Session.query(User).filter(
    User.firstname.like(search_var1),
    User.lastname.like(search_var2)
    )

使用filter_by()方法(操作符)

query = meta.Session.query(User).filter_by(
    firstname.like(search_var1),
    lastname.like(search_var2)
    )

链接使用filter()filter_by()操作符)

query = meta.Session.query(User).\
    filter_by(firstname.like(search_var1)).\
    filter_by(lastname.like(search_var2))

使用or_()and_()not()

from sqlalchemy import and_, or_, not_

query = meta.Session.query(User).filter(
    and_(
        User.firstname.like(search_var1),
        User.lastname.like(search_var2)
    )
)

3
这些不同方法之间是否存在值得注意的性能差异? - Miek
2
大多数不同的方法最终会生成相同的查询,因此在大多数情况下,您不会看到性能差异。 - Asa Stallard
3
我有点困惑。filter_by 文档中说,它是用于通过关键字参数进行过滤的:query(Foo).filter_by(bar='baz')。那么这与您在上面回答中使用的语法有何关系? - tel
1
filter_by 只接受一个参数。 - Kais Ben Daamech
为什么会选择一种方法而不是另一种? - Fabien Snauwaert

90

您可以简单地多次调用filter

query = meta.Session.query(User).filter(User.firstname.like(searchVar1)). \
                                 filter(User.lastname.like(searchVar2))

49
在大型MySQL表上,使用多个filter()方法和在单个filter中使用多个条件的组合(通过or_and_)之间是否存在性能差异? - exAres
10
多个filter调用会像逻辑上的“与”而不是“或”一样起作用吗? - danodonovan
11
我不会这么想-当你观察str(User.filter(cond1).filter(cond2))时,它生成的最终SQL仅将条件“and”连接起来。 - Shankar ARUL

74

您可以使用SQLAlchemy的or_函数在多个列中进行搜索(下划线是必须的,以区分它与Python自己的or)。

这里是一个例子:

from sqlalchemy import or_
query = meta.Session.query(User).filter(or_(User.firstname.like(searchVar),
                                            User.lastname.like(searchVar)))

10
你可以使用 | 运算符代替 or_,像这样 - (User.firstname.like(searchVar)) | (User.lastname.like(searchVar)),但你需要小心 | 的优先级,如果与比较运算符混合使用时没有括号,它可能会产生非常意外的结果。 - Daniel Kluev
1
难道不应该是 filter.or_(case1, case2) 吗? - fedorqui
2
这是错误的,因为问题是关于ORM的,但链接却指向表达式。 - user2846569
2
我之前使用了多个筛选语句,导致延迟明显增加。现在我已改用 or_ ,速度快了许多。感谢 @gclj5。 - Jimmy

6

一段通用的代码,适用于多个列。如果应用程序需要有条件地实现搜索功能,则也可以使用此代码。

search_key = 'abc'
search_args = [col.ilike('%%%s%%' % search_key) for col in ['col1', 'col2', 'col3']]
query = Query(table).filter(or_(*search_args))
session.execute(query).fetchall()

注意: %%在跳过查询中的%格式时非常重要。

1
啊,虽然这不是真正的问题,但这正是我在寻找的!+1 - Lucas Rahn
我使用Alchemy ORM,遇到了“str object has no attribute error”错误。 - Dat TT

1

为了使过滤器适用于全名,您可以修改查询以使用数据库引擎提供的concat函数。以下是如何更新您的代码以使其正常工作的示例:

from sqlalchemy import func

query = meta.Session.query(User).filter(func.concat(User.firstname, ' ', User.lastname).contains(searchVar))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接