Laravel Query Builder WHERE NOT IN
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Mastering WHERE NOT IN: Converting SQL Subqueries to Laravel Query Builder
As developers, we often transition between raw SQL and the expressive power of ORMs like Laravel's Query Builder or Eloquent. While SQL is powerful, translating complex logic, especially involving subqueries and negation (like NOT IN), into idiomatic PHP code requires understanding the specific methods the framework provides.
Today, we will dissect how to convert a common SQL patternâusing NOT IN with a subqueryâinto clean, efficient Laravel Query Builder syntax. Understanding this translation is key to writing maintainable and readable database interactions.
The Challenge: Translating NOT IN
You presented the following SQL query:
SELECT * FROM exams WHERE exams.id NOT IN (SELECT examId FROM testresults)The goal here is to retrieve all records from the exams table whose IDs do not appear in the set of examIds found in the testresults table. This is a classic exclusion problem.
While you could execute this directly in a database, when working within the Laravel ecosystem, we want to leverage Eloquent or the Query Builder methods for better abstraction and security.
Solution 1: The Direct Translation using whereNotIn
Fortunately, the Laravel Query Builder provides a dedicated method specifically for handling exclusion logic, which maps perfectly to the SQL NOT IN operator: the whereNotIn() method. This is the most direct and readable way to achieve your goal in Laravel.
To translate your specific query, assuming you are working within an Eloquent model context (let's say Exam model):
use App\Models\Exam;
$examsWithoutResults = Exam::whereNotIn('id', function ($query) {
// Subquery to select all IDs from the testresults table
$query->select('examId')->from('test_results');
})->get();Explanation of the Code
Exam::whereNotIn('id', ...): This tells the Query Builder we want to filter records where theexams.iddoes not match any value provided by the second argument.- The Subquery: The second argument is where the magic happens. We define a closure (a subquery) that executes:
select('examId')->from('test_results'). This effectively creates the list of all IDs present in thetest_resultstable.
This method cleanly encapsulates the logic, making it far more readable than raw SQL string concatenation. It leverages Laravel's ability to construct complex joins and subqueries abstractly, reinforcing why using framework tools like those found on laravelcompany.com is so beneficial for large applications.
Solution 2: Performance Consideration â The Anti-Join Alternative
While whereNotIn is syntactically perfect, itâs important to address performance, especially when dealing with massive tables. In some database systems or complex scenarios, using an anti-join patternâspecifically a LEFT JOIN combined with a WHERE IS NULL checkâcan offer superior performance over NOT IN.
For large datasets, you might consider rewriting the logic using a leftJoin:
use App\Models\Exam;
$examsWithoutResults = Exam::leftJoin('test_results', 'exams.id', '=', 'test_results.examId')
->whereNull('test_results.examId') // Select exams that did not find a match in test_results
->select('exams.*')
->get();When to Choose Which Method?
whereNotIn: Use this when the exclusion list is relatively small, or when code readability is the absolute top priority. It is the most direct translation of your original SQL intent.LEFT JOIN/WHERE IS NULL(Anti-Join): Use this when performance on very large tables is critical. Database optimizers are often highly tuned for join operations, and in many cases, a well-formed anti-join performs faster than the non-correlated subquery approach ofNOT IN.
Conclusion
Translating complex SQL patterns like WHERE NOT IN into Laravel requires shifting your mindset from writing raw database instructions to leveraging the frameworkâs expressive methods. By mastering methods like whereNotIn, you write code that is not only functional but also highly readable and maintainable. Always evaluate performance alongside readability; in the realm of robust application development, both are equally important when building scalable solutions on Laravel.