Laravel query builder upsert beats Eloquent line-by-line processing by ten times

Laravel Daily////3 min read

The performance gap in high-volume upserts

When managing e-commerce catalogs or large-scale data synchronizations, developers often face the challenge of merging a million-row CSV into an existing database. The choice of implementation strategy in Laravel determines whether a background job takes five minutes or thirty seconds. While the updateOrCreate() method is the most readable and developer-friendly, it creates a massive performance bottleneck because it executes individual database queries for every single row.

Prerequisites and Toolkit

To implement these high-performance patterns, you should be comfortable with PHP and the Laravel framework. Specifically, you need to understand how the Query Builder interacts with your database driver. For these benchmarks, MySQL was used on high-end hardware, but the logic applies across different environments.

Laravel query builder upsert beats Eloquent line-by-line processing by ten times
Laravel Upsert 1M Rows into MySQL from CSV: 5 Ways with Benchmarks

Key Libraries & Tools

  • Query Builder: A fluent interface for creating and running database queries without the overhead of the full ORM.
  • Eloquent ORM: Laravel's implementation of the active record pattern, useful for model-based logic.
  • MySQL: The primary database engine used for benchmarking these bulk operations.

Code Walkthrough: Implementing the Upsert

The most efficient way to handle massive updates is the native upsert() method. Unlike line-by-line processing, this sends data in batches, significantly reducing the round-trip time between the application and the database.

DB::table('products')->upsert([
    ['sku' => 'PROD-1', 'price' => 100, 'updated_at' => now()],
    ['sku' => 'PROD-2', 'price' => 150, 'updated_at' => now()],
], ['sku'], ['price', 'updated_at']);

The first argument is the array of values. The second argument, ['sku'], identifies the unique column that determines whether to insert or update. The third argument specifies which columns should be updated if a match is found.

Syntax Notes and Best Practices

A critical distinction exists between Eloquent ORM and the Query Builder when using upsert(). The Query Builder does not automatically manage created_at or updated_at timestamps. You must manually include these in your data array. Conversely, Eloquent ORM handles timestamps but bypasses model events and observers during an upsert. If your application relies on Saved or Created events to trigger secondary logic, the bulk upsert will break that workflow.

The failure of PHP-side smart splitting

One might assume that splitting data into "new" and "existing" sets within PHP before sending queries would save time. However, benchmarks prove this "smart split" approach is actually slower. Loading a million records into memory to perform comparisons in PHP consumes excessive RAM and adds roughly 20 seconds of overhead just for data preparation. Offloading logic to the database engine remains the gold standard for performance.

Topic DensityMention share of the most discussed topics · 16 mentions across 9 distinct topics
Query Builder
25%· products
Eloquent ORM
19%· products
Laravel
13%· products
MySQL
13%· products
DigitalOcean
6%· companies
Other topics
25%
End of Article
Source video
Laravel query builder upsert beats Eloquent line-by-line processing by ten times

Laravel Upsert 1M Rows into MySQL from CSV: 5 Ways with Benchmarks

Watch

Laravel Daily // 15:39

Tutorials, and demo projects with Laravel framework. Host: Povilas Korop

Who and what they mention most
Laravel
40.6%28
Filament
20.3%14
LiveWire
14.5%10
PHP
14.5%10
3 min read0%
3 min read