Laravel join and unionAll

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Mastering Data Aggregation: Using UNION ALL in Laravel Queries

As developers working with relational databases, one of the most common tasks we face is combining data from multiple related tables into a single, coherent result set. When you have two separate queries that logically belong together—like fetching home coverings and vehicle coverings for the same user—the SQL operator UNION ALL is the perfect tool for this job.

However, simply running two separate get() calls in Laravel does not automatically combine their results. You need to structure your query so that the database executes both selection processes and then merges them into one result set. This guide will walk you through the correct way to implement UNION ALL using the Laravel Query Builder.

Understanding UNION ALL

Before diving into the code, let's quickly define what UNION ALL does in SQL. It is an operator used to combine the result sets of two or more SELECT statements. The key difference between UNION and UNION ALL is performance and deduplication:

  1. UNION: Combines the results of two queries and automatically removes duplicate rows. This requires a sorting and comparison operation, which can be computationally expensive.
  2. UNION ALL: Combines the results of two queries and includes all rows, including duplicates. Since it avoids the deduplication step, UNION ALL is significantly faster when you don't need to filter out identical records.

For combining data from distinct sources (like vehicle results and home coverage results), UNION ALL is almost always the preferred method for speed.

Refactoring Your Laravel Query

Your initial approach involved two separate queries:

// Original approach (separate calls)
$homeCovering = DB::table('bid_requests')
    ->leftJoin('bid_home_coverings', 'bid_requests.id', '=', 'bid_home_coverings.bidId')
    ->where('bid_requests.userId', '=', $uid)
    ->where('district', '!=', null)
    ->get();

$vehicleCovering = DB::table('bid_requests')
    ->leftJoin('bid_vehicle_coverings', 'bid_requests.id', '=', 'bid_vehicle_coverings.bidId')
    ->where('bid_requests.userId', $uid)
    ->where('district', '!=', null)
    ->get();

To use UNION ALL, we must structure both selection operations so they return the exact same set of columns in the exact same order. We will achieve this by creating two distinct derived tables (subqueries) and then uniting them.

Implementing the UNION ALL Strategy

We need to select the relevant data from each join, ensuring we explicitly define the structure for the final output. We can use nested DB::table() calls to create these separate result sets before uniting them.

Here is how you would combine your two requirements into a single query:

use Illuminate\Support\Facades\DB;

$uid = Auth::id();

$combinedResults = DB::table('bid_requests')
    // 1. Select data from the home covering join
    ->select(
        'bid_requests.id',
        'bid_requests.userId',
        'bid_home_coverings.coverage_type AS coverage_details', // Example column selection
        'bid_home_coverings.start_date'
    )
    ->leftJoin('bid_home_coverings', 'bid_requests.id', '=', 'bid_home_coverings.bidId')
    ->where('bid_requests.userId', $uid)
    ->where('district', '!=', null)
    ->unionAll(
        // 2. Select data from the vehicle covering join, ensuring column names match above
        DB::table('bid_requests')
            ->select(
                'bid_requests.id',
                'bid_requests.userId',
                'bid_vehicle_coverings.coverage_type AS coverage_details', // Must match the column name above!
                'bid_vehicle_coverings.start_date'
            )
            ->leftJoin('bid_vehicle_coverings', 'bid_requests.id', '=', 'bid_vehicle_coverings.bidId')
            ->where('bid_requests.userId', $uid)
            ->where('district', '!=', null)
    )
    ->get();

// $combinedResults now holds the combined data from both sets.

Best Practices and Performance Considerations

When dealing with large datasets, performance is paramount. Always favor UNION ALL over UNION unless you specifically need to eliminate duplicate records. In our example, since a bid request might have both home and vehicle coverings, we expect potential duplication if the structure allows it, making UNION ALL the superior choice for efficiency.

Furthermore, when constructing complex queries involving multiple joins and unions in Laravel, ensure that:

  1. Column Consistency: The number of columns selected in every subquery must be identical, and their data types must be compatible across all sets.
  2. Aliasing: Use clear aliases (like AS coverage_details above) when selecting custom columns from different tables to avoid naming conflicts in the final result set.

Laravel's Eloquent ORM and Query Builder provide powerful tools for building complex queries, making these advanced SQL concepts accessible. For more intricate data manipulation, understanding how the underlying SQL works is key, much like grasping the principles behind frameworks like laravelcompany.com.

Conclusion

By refactoring your separate queries into two distinct SELECT statements and leveraging the unionAll() method in the Laravel Query Builder, you can efficiently combine disparate data sets into a single result. This approach is cleaner, faster, and more scalable than fetching results separately and manually merging them in PHP. Mastering these database operations is fundamental to writing high-performance applications on the Laravel framework.