Investigating the performance of Laravel’s whereIn vs whereIntegerInRaw
I recently came across a post claiming that Laravel’s ->whereIn() method is slow and that you should prefer ->whereIntegerInRaw() when working with integer IDs.
This claim appears to trace back to a 2016 blog post where the author reported that a ->whereIn() query with 10,000 IDs took ~4 seconds, and with 20,000 IDs ~15 seconds.
Notably, that post did not include comparable timings for ->whereIntegerInRaw().
Historically, I believe the root cause was not Laravel itself, but an old PDO bug related to parameter binding, which was fixed around 2020. Given that, I wanted to see whether the performance penalty still exists today - and if so, how significant it actually is.
What’s the difference between the two?
Firstly, let’s look at the actual difference between the two:
->whereIn() uses prepared statements, so the resulting query will look like this (assuming MySQL):
-- Step 1: Prepare the statement
PREPARE stmt FROM 'SELECT * FROM models WHERE id IN (?,?)';
SET @id1 = 1, @id2 = 2;
EXECUTE stmt USING @id1, @id2;
DEALLOCATE PREPARE stmt;
->whereIntegerInRaw() on the other hand casts every value in your array to (int) before simply adding them to the statement, so the resulting query will be a lot simpler and look like this:
SELECT *
FROM models
WHERE id IN (1,2)
Testing
I wanted to answer two questions:
- Is there still a measurable performance difference between
->whereIn()and->whereIntegerInRaw()in 2026? - If so, does the difference still scale non-linearly as the input size grows?
To test this, I wrote a small benchmark that queries between 1,000 and 50,000 IDs using both methods and compares execution time.
To isolate query performance, I used ->count() instead of ->get() to avoid the overhead of hydrating large collections, which would otherwise dominate the results.
Here’s the script:
foreach (
[
1_000,
10_000,
20_000,
50_000,
]
as $number
) {
echo "Number of Records: ".number_format($number).PHP_EOL;
$start = hrtime(true);
Model::query()
->whereIn('id', range(1, $number))
->count();
$durationWhereIn = hrtime(true) - $start;
printf('Duration: %s ms (whereIn)%s',
number_format($durationWhereIn / 1_000_000, 2),
PHP_EOL
);
$start = hrtime(true);
Model::query()
->whereIntegerInRaw('id', range(1, $number))
->count();
$durationRaw = hrtime(true) - $start;
printf('Duration: %s ms (whereIntegerInRaw)%s',
number_format($durationRaw / 1_000_000, 2),
PHP_EOL
);
printf('Time difference: %s ms / %s%%%s',
number_format(($durationWhereIn - $durationRaw) / 1_000_000, 2),
round(($durationWhereIn - $durationRaw) / $durationWhereIn * 100, 2),
PHP_EOL.PHP_EOL,
);
}
And the output:
Number of Records: 1,000
Duration: 67.10 ms (whereIn)
Duration: 4.06 ms (whereIntegerInRaw)
Time difference: 63.04 ms / 93.95%
Number of Records: 10,000
Duration: 109.93 ms (whereIn)
Duration: 9.54 ms (whereIntegerInRaw)
Time difference: 100.39 ms / 91.32%
Number of Records: 20,000
Duration: 134.75 ms (whereIn)
Duration: 18.03 ms (whereIntegerInRaw)
Time difference: 116.73 ms / 86.62%
Number of Records: 50,000
Duration: 226.52 ms (whereIn)
Duration: 41.59 ms (whereIntegerInRaw)
Time difference: 184.93 ms / 81.64%
Takeaways
Yes,
->whereIn()is slower than->whereIntegerInRaw(). This is expected:->whereIn()relies on parameter binding, which has non-zero overhead. That said, it’s still surprising to see differences approaching an order of magnitude in relative terms.The absolute difference is small. Even at 50,000 IDs, the gap is ~185 ms. In most real-world applications, this is unlikely to be the dominant bottleneck.
The relative difference decreases as the input grows. While the percentage difference is large for small sets, it steadily declines as the number of IDs increases. For very large
INclauses, the overhead of binding becomes less significant relative to the rest of the query execution. This is the opposite of the observation from 2016.In 2026, switching to
->whereIntegerInRaw()is probably no longer worth it. I feel that the readability and simplicity of->whereIn()generally outweigh the modest performance gain.
As always: benchmark in your own environment. Your database, driver, schema, and workload will matter far more than any micro-optimization shown here.