在SQLAlchemy中使用OR

303

我查看了文档,但是似乎找不到如何在 SQLAlchemy 中执行 OR 查询的方法。我只想执行这个查询。

SELECT address FROM addressbook WHERE city='boston' AND (lastname='bulger' OR firstname='whitey')

应该类似于

addr = session.query(AddressBook).filter(City == "boston").filter(????)
6个回答

525

来自教程:

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))

97
请注意,此方法支持使用生成器。因此,如果您有一个长列表需要进行OR操作,可以执行filter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl'))) - robru
108
@Robru提供的建议效率不高。如果你已经有了一个集合,那么你应该使用in_操作符,像这样:filter(User.name.in_(['Alice', 'Bob', 'Carl'])) - intgr
8
好的,谢谢。我之前不知道 SQLAlchemy 有那个筛选功能。 - robru
13
如果你想使用LIKE运算符而不是in_,那么robru展示的例子仍然有效。 - Lhassan Baazzi
5
我对Oracle的经验表明,使用一系列“OR”比使用“IN”要快得多。而且,“IN”仅限于大约1000个条目的集合,而“OR”则没有这个限制。 - g.a
显示剩余2条评论

470

SQLAlchemy重载了按位运算符&|~,因此可以使用这些运算符来代替使用or_()and_()等难以阅读的前缀语法(例如Bastien的答案):

.filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))

请注意,由于位运算符的优先级,括号不是可选的

因此,您的整个查询可能如下所示:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))

12
+1,但你能否将最后两个筛选参数用更多的括号包裹起来,并在它们与第一个参数之间使用&(而不是使用第二个filter调用)以达到相同的效果? - Chase Sandmann
30
可以,但可读性更高吗?不是。 - ThiefMaster
由于某些原因,如果一个操作数是集合,它就无法正常工作,但是使用and_()函数可以解决这个问题。例如:select(Author).outerjoin(Book, Author.books & (Book.pub_year == '2020')) - Ledorub

59

or_()函数在不知道OR查询组件数量的情况下很有用。

例如,假设我们正在创建一个带有几个可选过滤器的REST服务,如果任何一个过滤器返回true,则应返回记录。 另一方面,如果请求中未定义参数,则我们的查询不应更改。 如果没有or_()函数,则必须执行以下操作:

query = Book.query
if filter.title and filter.author:
    query = query.filter((Book.title.ilike(filter.title))|(Book.author.ilike(filter.author)))
else if filter.title:
    query = query.filter(Book.title.ilike(filter.title))
else if filter.author:
    query = query.filter(Book.author.ilike(filter.author))

使用or_()函数可以将其重写为:

query = Book.query
not_null_filters = []
if filter.title:
    not_null_filters.append(Book.title.ilike(filter.title))
if filter.author:
    not_null_filters.append(Book.author.ilike(filter.author))

if len(not_null_filters) > 0:
    query = query.filter(or_(*not_null_filters))

这个能够在一个列上使用 == 操作符吗?我看到了一些实现了 like、ilike 等方法,但是没有“equals”方法,除了 __eq__ 覆盖,它只返回一个布尔值,而不是一个可以放入 or_() 中的过滤器。 - theberzi
嗯,你不需要编写如此复杂的逻辑。你可以始终将你的连接词收集到一个临时变量中。也就是说,你可以将它们设置为 False(最初),然后使用 |= 将其他条件添加到它中。 - Matthias Urlichs

31

对于SQLAlchemy ORM 2.0,|or_ 都被接受。

文档

from sqlalchemy.future import select
from sqlalchemy.sql import or_


query = select(User).where(or_(User.name == 'ed', User.name == 'wendy'))
print(query)

# also possible:

query = select(User).where((User.name == 'ed') | (User.name == 'wendy'))
print(query)

4

如果需要应用“或”条件,可以将筛选条件存储在变量中,并使用管道链接它们。以下是一个例子

 class Student(db.Model):
     __tablename__ = "student"
     id = Column(Integer, primary_key=True)
     name = Column(String, nullable=False)

   def get_by_name(self, name):
      condition = # Code Here for condition
      filters = (Student.name == "Stack") | (Student.name == "Overflow") if 
condition else (Student.name == "Stack")
      query = Student.query.filter(filters).order_by(Student.id.asc())

4

这真的很有帮助。这是我为任何给定的表格实现的代码:

def sql_replace(self, tableobject, dictargs):

    #missing check of table object is valid
    primarykeys = [key.name for key in inspect(tableobject).primary_key]

    filterargs = []
    for primkeys in primarykeys:
        if dictargs[primkeys] is not None:
            filterargs.append(getattr(db.RT_eqmtvsdata, primkeys) == dictargs[primkeys])
        else:
            return

    query = select([tableobject]).where(and_(*filterargs))

    if self.r_ExecuteAndErrorChk2(query)[primarykeys[0]] is not None:
        # update
        filter = and_(*filterargs)
        query = tableobject.__table__.update().values(dictargs).where(filter)
        return self.w_ExecuteAndErrorChk2(query)

    else:
        query = tableobject.__table__.insert().values(dictargs)
        return self.w_ExecuteAndErrorChk2(query)

# example usage
inrow = {'eqmtvs_id': eqmtvsid, 'datetime': dtime, 'param_id': paramid}

self.sql_replace(tableobject=db.RT_eqmtvsdata, dictargs=inrow)

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