Laravel whereRaw vs selectRaw: Best Practices for Raw SQL in the Query Builder
Use whereRaw to inject raw SQL conditions into the WHERE clause for complex filtering, and selectRaw to add calculated columns or database-specific functions to the SELECT list, always binding parameters to prevent SQL injection.
When building complex database queries in the laravel/framework repository, developers often encounter scenarios that exceed the expressive capabilities of the standard query builder. Understanding how to safely implement a where clause with raw SQL statements using whereRaw, and how it architecturally differs from selectRaw, is essential for writing secure, maintainable Laravel applications.
How whereRaw Works in the Query Builder
The whereRaw method allows you to inject a raw SQL string directly into the WHERE clause of your query. According to the Laravel source code, this method is defined in src/Illuminate/Database/Query/Builder.php around line 1233.
When you call whereRaw, the builder pushes an array containing the raw SQL onto the $wheres collection:
['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean]
The method then registers any supplied bindings via addBinding, ensuring that parameters remain separate from the SQL command itself. During query compilation, the Grammar::whereRaw method in src/Illuminate/Database/Query/Grammars/Grammar.php (around line 279) simply returns the raw SQL string while the grammar engine handles the bound parameters.
Binding Safety with whereRaw
Always use parameter bindings with whereRaw to protect against SQL injection. The framework stores these bindings under the 'where' key in the bindings array.
// Safe: uses parameter binding
$users = DB::table('users')
->whereRaw('JSON_EXTRACT(meta, "$.age") > ?', [30])
->get();
// Unsafe: string concatenation
$users = DB::table('users')
->whereRaw("JSON_EXTRACT(meta, '$.age') > $age")
->get();
How selectRaw Differs from whereRaw
While whereRaw targets the WHERE clause, selectRaw targets the SELECT list. Defined in src/Illuminate/Database/Query/Builder.php around line 348, this method creates a new Expression instance and adds it to the $columns collection.
The key architectural difference lies in the query phase each method affects:
whereRawfilters which rows are returned (the WHERE clause)selectRawdetermines what data is returned for each row (the SELECT clause)
Bindings for selectRaw are stored under the 'select' key, separate from WHERE bindings.
Practical selectRaw Usage
Use selectRaw for calculated columns, database-specific functions, or sub-queries in the SELECT list:
// Calculated columns with database functions
$stats = DB::table('orders')
->select('customer_id')
->selectRaw('COUNT(*) as order_count')
->selectRaw('SUM(total) as lifetime_value')
->groupBy('customer_id')
->get();
// Using database-specific JSON functions
$users = DB::table('users')
->select('id')
->selectRaw('JSON_EXTRACT(preferences, "$.theme") as theme')
->get();
Key Differences Between whereRaw and selectRaw
Understanding the distinct roles of these methods prevents architectural confusion when constructing queries.
| Aspect | whereRaw |
selectRaw |
|---|---|---|
| Target clause | WHERE | SELECT |
| Primary function | Filters rows | Defines return columns |
| Binding key | 'where' |
'select' |
| Compilation | Grammar::whereRaw |
Expression object in $columns |
| Return impact | Boolean filtering | Column values/expressions |
Best Practices for Raw SQL in Where Clauses
When you need functionality beyond the standard query builder methods, follow these guidelines to maintain security and portability.
Prefer Dedicated Builder Methods First
Before reaching for whereRaw, check if Laravel provides a dedicated method. The framework includes specialized methods for common operations that handle database-specific syntax automatically:
whereJsonContains()for JSON querieswhereColumn()for comparing columnswhereBitwise()for bitwise operationswhereDate(),whereMonth(), etc. for date filtering
Only use whereRaw when the builder lacks native support for your specific condition.
Always Use Parameter Bindings
Never concatenate user input or variables directly into raw SQL strings. Laravel's binding system protects against SQL injection by separating data from commands.
// Correct: parameterized query
$query->whereRaw('price > ? AND quantity < ?', [$minPrice, $maxQty]);
// Incorrect: string interpolation (vulnerable)
$query->whereRaw("price > $minPrice AND quantity < $maxQty");
Mix Raw Methods with Standard Builder Chains
whereRaw and selectRaw integrate seamlessly with other query builder methods. You can combine them with standard where(), join(), and orderBy() calls:
$results = DB::table('products')
->select('id', 'name')
->selectRaw('(price - cost) as margin')
->where('active', true)
->whereRaw('stock_level < reorder_point')
->orderBy('margin', 'desc')
->get();
Document Database-Specific Dependencies
Raw SQL often uses database-specific functions like JSON_EXTRACT (MySQL) or STRING_AGG (PostgreSQL). When using these, document the dependency in comments or documentation to ensure portability concerns are visible during database migrations.
Summary
whereRawinjects raw SQL into the WHERE clause viasrc/Illuminate/Database/Query/Builder.php, storing bindings under the'where'key and compiling throughGrammar::whereRawinsrc/Illuminate/Database/Query/Grammars/Grammar.php.selectRawadds raw expressions to the SELECT list, creatingExpressionobjects stored in the$columnscollection with bindings under the'select'key.- Security: Always use parameter bindings with
whereRawto prevent SQL injection; never concatenate variables directly into raw SQL strings. - Portability: Prefer dedicated builder methods (
whereJsonContains,whereColumn) before resorting to raw SQL, and document any database-specific functions used in raw expressions.
Frequently Asked Questions
Is whereRaw safe from SQL injection?
whereRaw is safe only when you use parameter bindings. The method accepts an optional second argument for bindings, which Laravel processes through addBinding in src/Illuminate/Database/Query/Builder.php and separates from the SQL command during compilation in Grammar::whereRaw. If you concatenate variables directly into the SQL string, you bypass this protection and become vulnerable to SQL injection attacks.
When should I use whereRaw instead of regular where clauses?
Use whereRaw only when Laravel's dedicated query builder methods cannot express your condition. According to the src/Illuminate/Database/Query/Builder.php source, you should first check for native methods like whereJsonContains, whereColumn, whereBitwise, or date-specific methods. Reserve whereRaw for complex JSON path queries, bitwise operations on unsupported databases, or custom database functions that lack builder abstractions.
How do I combine whereRaw with other query builder methods?
whereRaw integrates seamlessly with standard builder methods because it returns the Builder instance. You can chain whereRaw with where(), orWhere, join(), orderBy(), and other methods just like any other query builder method. The raw condition gets added to the $wheres collection alongside standard conditions, and the grammar compiler processes them in order during SQL generation in src/Illuminate/Database/Query/Grammars/Grammar.php.
What is the difference between whereRaw and havingRaw?
whereRaw filters rows before aggregation in the WHERE clause, while havingRaw filters results after aggregation in the HAVING clause. In src/Illuminate/Database/Query/Builder.php, whereRaw adds conditions to the $wheres array processed during the initial row filtering phase, whereas havingRaw adds to the $havings array processed after GROUP BY operations. Use whereRaw to filter which rows enter the calculation, and havingRaw to filter aggregated results like SUM(total) > 1000.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →