SQLAlchemy
SQL
database
OR operation
Python

Using OR in SQLAlchemy

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In SQLAlchemy, combining conditions with logical OR is common when search filters can match multiple fields or states. The core tools are or_() from SQLAlchemy and the overloaded bitwise OR operator on expression objects. Choosing one consistent style keeps query logic readable and easier to debug.

Building OR Conditions with or_

The most explicit approach is sqlalchemy.or_. It accepts any number of boolean expressions and returns one grouped SQL condition.

python
1from sqlalchemy import create_engine, Column, Integer, String, or_
2from sqlalchemy.orm import declarative_base, Session
3
4Base = declarative_base()
5
6class User(Base):
7    __tablename__ = 'users'
8    id = Column(Integer, primary_key=True)
9    email = Column(String)
10    status = Column(String)
11
12engine = create_engine('sqlite:///:memory:')
13Base.metadata.create_all(engine)
14
15with Session(engine) as s:
16    s.add_all([
17        User(email='[email protected]', status='active'),
18        User(email='[email protected]', status='pending'),
19        User(email='[email protected]', status='disabled'),
20    ])
21    s.commit()
22
23    q = s.query(User).filter(or_(User.status == 'active', User.status == 'pending'))
24    print([u.email for u in q.all()])

or_ is clear to most readers and avoids precedence confusion in long filters.

Using Operator Syntax Carefully

SQLAlchemy also supports expression composition with | and &, which can look concise. If you use this style, always keep parentheses around each comparison to avoid precedence bugs.

python
1with Session(engine) as s:
2    condition = (User.status == 'active') | (User.email.like('%@example.com'))
3    rows = s.query(User).filter(condition).all()
4    print(len(rows))

Without parentheses, Python may evaluate parts of the expression in unexpected order. This can produce incorrect SQL or runtime errors that are hard to trace.

Dynamic Query Construction

Real filters are often optional. Build a list of conditions and join them with or_ only when necessary.

python
1from sqlalchemy import or_
2
3def search_users(session: Session, status: str | None, email_fragment: str | None):
4    clauses = []
5    if status:
6        clauses.append(User.status == status)
7    if email_fragment:
8        clauses.append(User.email.ilike(f'%{email_fragment}%'))
9
10    query = session.query(User)
11    if clauses:
12        query = query.filter(or_(*clauses))
13    return query.all()

This pattern scales well when API parameters are optional. It also keeps business rules near query assembly rather than spreading ad hoc SQL across handlers.

For SQLAlchemy two point zero style, the same principles apply with select(User).where(or_(...)). The syntax differs slightly, but condition building rules stay the same.

Inspecting Generated SQL and Performance

Readable expression code is only half the job. You should also inspect generated SQL for critical queries to confirm predicate grouping and index usage. SQLAlchemy can compile statements to text, which is useful in tests and review.

python
1from sqlalchemy import select
2
3stmt = select(User).where(or_(User.status == 'active', User.status == 'pending'))
4print(stmt)

After confirming correctness, check database execution plans for large tables. OR predicates sometimes prevent efficient index usage depending on database and query shape. In some workloads, replacing wide OR chains with normalized lookup tables or union queries improves performance and keeps plans stable as data volume grows.

When OR conditions compare one column against many constants, consider in_ for clarity and potential planner improvements. For example, User.status.in_(["active", "pending"]) is often easier to read than two equality clauses. Use whichever form communicates intent most clearly for your team.

Document query intent in repository methods so future edits do not accidentally change OR semantics in sensitive authorization paths.

Common Pitfalls

  • Mixing Python or with SQLAlchemy expressions, which evaluates in Python instead of SQL.
  • Forgetting parentheses when using |, causing precedence related bugs.
  • Building OR chains directly from unvalidated user input.
  • Applying OR logic where AND logic was intended in security sensitive filters.
  • Creating huge OR lists instead of using IN for simple equality sets.

Summary

  • Use or_() for explicit and maintainable OR conditions.
  • If using |, wrap each expression in parentheses.
  • Build clauses dynamically for optional filters.
  • Validate user driven filter values before query assembly.
  • Prefer IN for many equal value checks when appropriate.

Course illustration
Course illustration

All Rights Reserved.