SQLAlchemy: How to Sort Query Results (ASC, DESC)

Updated: January 3, 2024 By: Guest Contributor Post a comment

Introduction

Sorting query results is an essential operation in database systems, and SQLAlchemy, the Python SQL toolkit, offers an intuitive way to order data in both ascending (ASC) and descending (DESC) order. This guide will take you through the basics to more advanced usage of sorting in SQLAlchemy.

Basic Sorting with SQLAlchemy

To sort query results in SQLAlchemy, you can use the order_by() method. This method allows you to specify the column by which you want to sort the results. By default, order_by() sorts in ascending order.

from sqlalchemy import create_engine, Column, Integer, String, asc
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Integer)

engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()

# Insert example records - omitted for brevity
# Sort users by age in ascending order
sorted_users = session.query(User).order_by(User.age).all()

When you execute the above query, you’ll receive a list of User objects sorted by their age in ascending order.

Descending Order Sort

To sort the records in descending order, you can use the desc() function provided by SQLAlchemy:

from sqlalchemy import desc

# Sort users by age in descending order
sorted_users_desc = session.query(User).order_by(desc(User.age)).all()

This query will return the users ordered by their ages from the highest (oldest) to the lowest (youngest).

Sorting by Multiple Criteria

In many cases, you might want to sort the results by multiple columns. SQLAlchemy makes it easy to specify secondary criteria for sorting:

# Sort users by name in ascending order, then by age in descending order
sorted_users_multi = session.query(User).order_by(User.name, desc(User.age)).all()

Here, the results will first be sorted by user name in ascending order and then by age in descending order within each group of users with the same name.

Join Queries and Sorting

Sorting becomes even more powerful when used with joined tables. Assume we have another table, Addresses, and we want to join it with Users and then sort the results.

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'))
    email = Column(String)

# Query with join
users_with_addresses = session.query(User, Address).join(Address).order_by(User.name, Address.email).all()

In the joined query, the results are sorted by the user name and then by the corresponding email address in the addresses table.

Using the func Module for Advanced Sorts

SQLAlchemy provides the func module that allows the use of SQL functions in your queries. For sorting, you could use functions like COUNT(), MAX(), and others.

from sqlalchemy import func

# Sorting based on the count of a user's addresses
user_address_counts = session.query(User.name, func.count(Address.id).label('address_count')).join(Address).group_by(User.name).order_by('address_count').all()

This query will sort the users based on the number of addresses they have.

Dynamic Sorting

In web applications, you often need dynamic sorting based on user input. SQLAlchemy can dynamically build the order criteria:

sort_column = 'age'  # Assume this comes from user input

if sort_column == 'age':
    order_criteria = User.age.asc()
else:
    order_criteria = User.name.asc()

users_dynamic_sort = session.query(User).order_by(order_criteria).all()

You can alter the order_criteria based on the user input and then use it in the order_by() method.

Sorting with Nulls

When dealing with nullable columns, you might want to control how null values are ordered in your results, using nullslast() or nullsfirst():

# Sort ages with nulls at the end
sorted_users_nulls_last = session.query(User).order_by(User.age.nullslast()).all()

Chaining order_by() Calls

If your sorting is conditional or complex, you might need to chain multiple order_by() calls. Each subsequent order_by() call will overwrite the previous one unless you are using the Query object’s add_order_by() method:

# This will sort users only by age
chained_query = session.query(User).order_by(User.name).order_by(User.age)

# This will sort users first by name, then by age
chained_query_with_add = session.query(User).order_by(User.name).add_order_by(User.age)

Conclusion

In summary, sorting in SQLAlchemy is flexible and can be adapted to suit simple to complex scenarios. Through the use of order_by(), desc(), func, and other SQLAlchemy features, we can sort query results in any order we require. Remember to always consider the best index utilization for your database to ensure efficient querying.