Skip to content
tezvyn:
Menu

SQLAlchemy: Control When Your Relationships Load

Source: docs.sqlalchemy.orgHardHow cards are made

SQLAlchemy's default lazy loading is convenient but can cause an N+1 query storm. Use eager loading (joinedload, selectinload) for collections you'll access to prevent many database round trips.

Why it exists

An ORM maps database rows to objects. A User object might relate to a list of Address objects. The core problem is deciding when to fetch those addresses. Fetching them all upfront can be slow and wasteful if they aren't used. Fetching them one-by-one on demand can lead to the infamous N+1 query problem. Relationship loading strategies give you explicit control over this trade-off.

The mental model

Think of it as packing for a trip. Lazy loading is packing light and buying things as you need them—a fast departure, but many small, slow trips to the store later. Eager loading is packing everything you anticipate needing upfront—a slower departure, but no extra trips. The right choice depends on what you plan to do at your destination.

How it works

SQLAlchemy offers several strategies, specified via query options. The default is lazy='select', which issues a new SELECT statement the first time you access a related attribute on an object. This is the source of the N+1 problem. Eager loading strategies fetch the data upfront. joinedload() uses a SQL JOIN in the main query to fetch parent and child objects together. selectinload() issues a second query using the primary keys from the first query to fetch all related objects at once. raiseload() is a guard: it prevents access entirely by throwing an error, stopping accidental lazy loads that would harm performance.

When to use it

Use eager loading (joinedload or selectinload) when you know you will access the related objects for every parent object in your result set, especially inside a loop. This is the classic fix for the N+1 query problem. Use raiseload in APIs to enforce performance contracts and prevent unexpected database hits from code changes.

When not to use it

Don't use eager loading if you only occasionally access the related objects; the upfront cost of the larger query isn't worth it. Avoid joinedload for large to-many collections, as the JOIN can create a Cartesian product, returning many redundant rows from the database and consuming excess memory. In these cases, selectinload is almost always a better choice for to-many relationships.

One canonical example

A common bug is fetching users and displaying their addresses: for user in users: print(user.addresses). This triggers one query for users, plus N additional queries for each user's addresses. Using select(User).options(selectinload(User.addresses)) fetches all users in one query and all their addresses in a single second query, eliminating the N+1 storm.

Interview question

When querying a "to-many" relationship in SQLAlchemy, under which condition is selectinload() generally preferred over joinedload()?

  • a.When the application requires all related objects to be loaded in a single database round trip.
  • b.When the related data is small and can be efficiently fetched in the primary query.
  • c.When the "to-many" collection is potentially large, to avoid performance issues from a Cartesian product.Correct
  • d.When the relationship should never be loaded, and any access attempt must raise an exception.
Why?

selectinload() is preferred for large "to-many" relationships because joinedload() uses a SQL JOIN, which can create a Cartesian product, leading to excessive data transfer. selectinload() avoids this by issuing a separate SELECT query for the related objects, making it more efficient in such scenarios.

Just read this? Test yourself on what you have been reading.

Read the original → docs.sqlalchemy.org

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on python — each one lists the topics its interview covers.

See open roles