Medium-High Impact

Eliminate N+1 Queries with Batch Loading

10-100x faster queries by batching related data loads

Why This Matters

The N+1 query problem occurs when you fetch a list of records, then loop through them to fetch related data. This results in 1 query for the list plus N queries for related data - hence "N+1".

This pattern is extremely common and causes:

Warning: Loading 1000 posts with their authors using N+1 queries can take 5+ seconds. Batch loading reduces this to under 50ms.

The Problem

Looping through results and querying related data for each item creates N+1 queries:

Incorrect - N+1 query pattern
-- Query 1: Get all posts
SELECT * FROM posts WHERE published = true;
-- Returns 100 posts

-- Application code loops through posts:
for (post in posts) {
  -- Query 2-101: Get author for each post
  SELECT * FROM users WHERE id = post.author_id;
}

-- Result: 1 + 100 = 101 queries!
-- Time: ~2 seconds for 100 posts

Each iteration triggers a separate database query, creating massive overhead. With 1000 posts, this becomes 1001 queries.

The Solution

Use JOIN to fetch related data in a single query:

Correct - Single query with JOIN
-- Single query fetches posts and authors together
SELECT 
  p.*,
  u.id AS author_id,
  u.name AS author_name,
  u.email AS author_email
FROM posts p
JOIN users u ON u.id = p.author_id
WHERE p.published = true;

-- Result: 1 query for all data
-- Time: ~20ms for 100 posts

When you can't use JOIN (e.g., loading data conditionally), use ANY(array) to batch load:

-- Step 1: Get all posts
SELECT * FROM posts WHERE published = true;
-- Returns 100 posts with author_ids: [1, 2, 3, ..., 100]

-- Step 2: Batch load all authors in one query
SELECT * FROM users
WHERE id = ANY(ARRAY[1, 2, 3, ..., 100]);

-- Result: 2 queries total (instead of 101)
-- Time: ~25ms for 100 posts

Best Practice: Use JOIN when you always need related data. Use ANY(array) when loading is conditional or when you need to load multiple different relationships.

Result: Batch loading reduces query time from 2 seconds to 20ms - a 100x improvement. The difference grows even larger with more records.

Check Your Project Automatically

Connect your Supabase project to detect N+1 query patterns and other performance issues.

Start Supabase Audit

Common Mistakes

Implementation Examples

Here's how to implement batch loading in common scenarios:

-- Example: Loading posts with comments count
SELECT 
  p.*,
  COUNT(c.id) AS comment_count
FROM posts p
LEFT JOIN comments c ON c.post_id = p.id
WHERE p.published = true
GROUP BY p.id;
-- Example: Loading multiple relationships with ANY(array)
-- Step 1: Get posts
SELECT id, author_id, category_id FROM posts;

-- Step 2: Batch load authors
SELECT * FROM users
WHERE id = ANY(ARRAY[1, 2, 3]);

-- Step 3: Batch load categories
SELECT * FROM categories
WHERE id = ANY(ARRAY[10, 20, 30]);

-- Result: 3 queries total instead of 1 + N + M queries

Learn More