Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Lesson 8 of the Advanced Laravel: Architecture, Scaling & Performance course
LaravelJune 27, 20264 min read

Advanced Subqueries and Joins for Laravel Performance

Master nested subqueries and `joinSub` in Laravel to optimize complex reports and push logic to the database, ensuring your SaaS platform remains performant.

LaravelSQLDatabasePerformanceEloquentArchitecturephpbackend

Previously in this course, we explored querying with strict Eloquent to maintain data integrity and prevent common performance pitfalls. While strict loading is essential, scaling a high-traffic SaaS platform often requires moving beyond standard ORM methods. When reports require calculating aggregates across millions of rows, pulling data into PHP memory is a non-starter.

In this lesson, we’ll use SQL subqueries and the joinSub method to push complex filtering and aggregation logic directly into the database engine.

The Performance Cost of Application-Layer Logic

In our running project, we often need to display a dashboard showing "The most active user per subscription plan." If you fetch all users and their subscription counts into a collection to sort them in PHP, you'll likely hit memory limits or latency spikes as the user base grows.

We need the database to do the heavy lifting. By nesting a subquery, we can isolate the aggregation logic and join it against our primary tables, ensuring the database only returns the precise result set we need.

Understanding Nested Subqueries

A subquery is essentially a query within a query. In Eloquent, we can define a subquery using a closure that returns a Query\Builder instance. This is particularly useful for selecting calculated values that depend on related tables.

Consider this requirement: Fetch all users along with the date of their most recent login.

PHP
use App\Models\User;
use Illuminate\Support\Facades\DB;

$latestLogins = DB::table('logins')
    ->select('user_id', DB::raw('MAX(created_at) as last_login_at'))
    ->groupBy('user_id');

$users = User::query()
    ->joinSub($latestLogins, 'latest_logins', function ($join) {
        $join->on('users.id', '=', 'latest_logins.user_id');
    })
    ->get();

By using joinSub, we treat the subquery result like a temporary table. This is significantly more efficient than running a separate query for every user or using a correlated subquery in the select statement, which can lead to slow execution plans.

Real-World Optimization with joinSub

Let’s advance our project. We need to generate a monthly report showing the total revenue per tenant, but only for those who have exceeded a specific transaction volume.

Instead of iterating through collections, we define a subquery that identifies the heavy-hitting tenants, then join that result set back to our Tenants table.

PHP
public function getHighVolumeTenantReport(int $minTransactions)
{
    #6A9955">// 1. Define the subquery for high-volume transactions
    $subQuery = DB::table('transactions')
        ->select('tenant_id', DB::raw('SUM(amount) as total_revenue'))
        ->where('status', 'completed')
        ->groupBy('tenant_id')
        ->havingRaw('COUNT(*) > ?', [$minTransactions]);

    #6A9955">// 2. Join the subquery to the main Tenants model
    return Tenant::query()
        ->joinSub($subQuery, 'tenant_revenue', function ($join) {
            $join->on('tenants.id', '=', 'tenant_revenue.tenant_id');
        })
        ->select('tenants.*', 'tenant_revenue.total_revenue')
        ->orderByDesc('tenant_revenue.total_revenue')
        ->get();
}

This approach keeps our memory footprint constant, regardless of whether we have 100 or 1,000,000 transactions, because the database filters the data before it ever reaches our application.

Comparison: Eloquent vs. JoinSub

MethodApproachPerformanceMemory Usage
Collection/MapPull all records into PHPPoorHigh
Correlated SubqueryQuery inside select()ModerateLow
joinSubTemp table joinExcellentLow

Hands-on Exercise

Refactor an existing report in your project that currently uses a foreach loop to calculate totals. Replace the logic by creating a dedicated method using joinSub.

  1. Identify the aggregate calculation (e.g., SUM, COUNT, AVG).
  2. Write the subquery in a separate variable.
  3. Use ->joinSub($subQuery, 'alias', ...) to attach it to your main query.
  4. Verify the performance improvement by checking the query log via DB::enableQueryLog().

Common Pitfalls

  • Alias Collision: Always provide a descriptive alias for your joinSub. If your application grows, you might end up joining multiple subqueries, and ambiguous column names will cause SQL errors.
  • Missing Indexes: Even with a perfect subquery, if the join keys (e.g., tenant_id) aren't indexed, the database will perform a full table scan. Refer back to our Database Indexing Strategies to ensure your join columns are covered.
  • Over-complication: Don't use subqueries for logic that can be handled by standard Eloquent relationships or simple JOIN statements. If a withCount or withSum suffices, use those; they are more readable and maintainable.

By pushing logic to the database, you reduce the bridge between your app and the data, which is the single most effective way to scale a high-traffic Laravel application. We've moved beyond simple CRUD; we are now treating the database as a powerful processing engine.

Up next: We will dive into Raw Expressions for Performance, where we'll learn when and how to drop down to native SQL to squeeze out every drop of performance from our queries.

Previous lessonQuerying with Strict EloquentNext lesson Raw Expressions for Performance
Back to Blog

Similar Posts

LaravelJune 28, 20264 min read

Database Indexing for Joins: Architecting High-Performance Queries

Master SQL indexing for joins by learning to analyze execution plans and build covering indexes that eliminate table scans in high-traffic Laravel applications.

Read more
LaravelJune 26, 20264 min read

Database Indexing Strategies: Optimizing Laravel Query Performance

Master database indexing strategies to boost performance. Learn to analyze execution plans, implement composite indexes, and optimize complex search queries.

Part of the course

Advanced Laravel: Architecture, Scaling & Performance

advanced · Lesson 8 of 57

  1. 1

    Transitioning from MVC to DDD

    3 min
  2. 2

    Defining Bounded Contexts

    3 min
  3. 3

    Implementing Action Classes

    3 min
Read more
LaravelJune 28, 20263 min read

Database Query Caching Layers: Optimizing Laravel Performance

Learn how to implement database query caching in Laravel to reduce server load. Master cache eviction strategies to ensure data integrity in high-traffic apps.

Read more
4

Utilizing Data Transfer Objects (DTOs)

3 min
  • 5

    Service Layer Pattern

    4 min
  • 6

    Modular Monolith Structure

    3 min
  • 7

    Querying with Strict Eloquent

    4 min
  • 8

    Advanced Subqueries and Joins

    4 min
  • 9

    Raw Expressions for Performance

    4 min
  • 10

    Advanced Indexing Strategies

    4 min
  • 11

    Database Partitioning Techniques

    4 min
  • 12

    Read/Write Database Splitting

    4 min
  • 13

    Handling Multi-Database Connections

    3 min
  • 14

    Eloquent Caching Strategies

    3 min
  • 15

    Queue Worker Prioritization

    4 min
  • 16

    Unique Job Patterns

    4 min
  • 17

    Rate Limiting Background Jobs

    3 min
  • 18

    Event-Driven Architecture

    4 min
  • 19

    Integrating External Message Brokers

    4 min
  • 20

    Distributed Transactions and Sagas

    3 min
  • 21

    Eventual Consistency Patterns

    4 min
  • 22

    Multi-Layered Caching Strategy

    4 min
  • 23

    Cache Tagging and Invalidation

    4 min
  • 24

    Session Persistence in Clusters

    4 min
  • 25

    High-Availability Infrastructure

    4 min
  • 26

    Zero-Downtime Deployment Pipelines

    4 min
  • 27

    Advanced OAuth2 Implementation

    3 min
  • 28

    JWT and Stateless Security

    4 min
  • 29

    Multi-Tenant Security Isolation

    3 min
  • 30

    Defense Against SSRF

    3 min
  • 31

    Mass Assignment Hardening

    4 min
  • 32

    Automated Security Testing

    3 min
  • 33

    Custom Telemetry Design

    3 min
  • 34

    Distributed Tracing

    4 min
  • 35

    Profiling PHP Execution

    3 min
  • 36

    Memory Management in Long-Running Processes

    4 min
  • 37

    Testing DDD Components

    3 min
  • 38

    Contract Testing

    3 min
  • 39

    Handling Large File Uploads

    3 min
  • 40

    Optimizing Asset Pipelines

    4 min
  • 41

    Database Query Caching Layers

    3 min
  • 42

    Advanced Eloquent Scopes

    4 min
  • 43

    Distributed Locks

    3 min
  • 44

    API Versioning Strategies

    4 min
  • 45

    Database Migration Strategies

    4 min
  • 46

    Handling Webhooks Securely

    3 min
  • 47

    Advanced Logging Patterns

    3 min
  • 48

    Database Indexing for Joins

    4 min
  • 49

    Graceful Degradation

    3 min
  • 50

    Custom Middleware Development

    Coming soon
  • 51

    Database Connection Pooling

    Coming soon
  • 52

    Handling Large Data Exports

    Coming soon
  • 53

    Security Header Configuration

    Coming soon
  • 54

    Database Sharding Concepts

    Coming soon
  • 55

    Real-time Data Synchronization

    Coming soon
  • 56

    Database Deadlock Prevention

    Coming soon
  • 57

    Managing Third-Party API Integrations

    Coming soon
  • View full course