Question

How to Get Raw SQL from Laravel Query Builder

phpsqllaraveleloquentlaravel-query-builder

Question

Given the following Laravel query builder code:

DB::table('users')->get();

I want to inspect the raw SQL query string that this builder generates. In this example, I expect something like:

SELECT * FROM users

How can I get the SQL string from a Laravel query builder query?

Short Answer

By the end of this page, you will understand how Laravel's query builder builds SQL, how to get the SQL template using toSql(), how parameter bindings work, and how developers typically debug and inspect queries in real Laravel projects.

Concept

Laravel's query builder does not immediately run SQL when you start chaining methods like table(), where(), or orderBy(). Instead, it builds a query object step by step.

When you finally call methods like get(), first(), insert(), update(), or delete(), Laravel compiles that query object into SQL and sends it to the database.

To inspect the SQL before execution, Laravel provides:

  • toSql() — returns the SQL string with placeholders such as ?
  • getBindings() — returns the values that will be inserted into those placeholders

This matters because in real development you often need to:

  • debug a query that is not returning expected results
  • verify filters and joins
  • optimize performance
  • log generated SQL
  • compare what Laravel builds against handwritten SQL

A key detail: the SQL returned by toSql() is usually a prepared statement template, not a fully interpolated final SQL string. That means values are often shown as ? placeholders instead of being inserted directly into the string.

Mental Model

Think of the query builder like filling out a restaurant order form.

  • DB::table('users') chooses the menu section
  • where(...) adds conditions to the order
  • orderBy(...) adds sorting instructions
  • toSql() shows the printed order ticket template
  • getBindings() shows the actual values that will be attached to that ticket
  • get() sends the order to the kitchen

So toSql() shows the structure of the query, while getBindings() shows the data that will be plugged into it.

Take Quiz

Syntax and Examples

Basic syntax

Use toSql() on the builder before executing it:

$sql = DB::table('users')->toSql();

echo $sql;

Output:

select * from `users`

Depending on your database driver, quoting may vary. For example, MySQL often uses backticks.

Important: do not call get() first

This will not work the way you expect:

DB::table('users')->get()->toSql();

Why? Because get() executes the query and returns a collection, not a query builder.

Query with bindings

$query = DB::table('users')->where('email', 'test@example.com');

$sql = $query->toSql();
$bindings = $query->getBindings();

var_dump($sql);
var_dump($bindings);

Possible output:

string(38) "select * from `users` where `email` = ?"
array(1) {
  [0]=>
  string(16) "test@example.com"
}

Quick debugging

$query = DB::table('users')->where('active', 1);

dd($query->toSql(), $query->getBindings());

This is a common Laravel debugging pattern.

Step by Step Execution

Consider this example:

$query = DB::table('users')
    ->where('active', 1)
    ->orderBy('name');

dd($query->toSql(), $query->getBindings());

Step 1: DB::table('users')

Laravel creates a query builder for the users table.

Step 2: where('active', 1)

Laravel adds a filter condition.

At this point, the builder knows:

  • the table is users
  • there is a where clause on active
  • the value 1 is stored as a binding

Step 3: orderBy('name')

Laravel adds sorting to the query definition.

Step 4: toSql()

Laravel compiles the builder into a SQL template:

select * from `users` where `active` = ? order by `name` asc

Step 5: getBindings()

Laravel returns the values for the placeholders:

[1]

Step 6: dd(...)

Laravel dumps both values so you can inspect the query before it runs.

If you had called get() instead, Laravel would execute the SQL and return actual rows.

Real World Use Cases

Developers inspect raw SQL in Laravel for many practical reasons:

Debugging incorrect results

If a query returns too many or too few rows, checking the SQL helps confirm whether the conditions were built correctly.

Performance tuning

You may want to copy the SQL into your database client and run EXPLAIN to inspect indexes and query cost.

Logging slow queries

Applications often log generated SQL during development or when investigating production issues.

Verifying dynamic filters

If your app builds queries from request parameters, inspecting SQL helps confirm that optional filters are applied correctly.

Learning SQL through Laravel

Beginners often use toSql() to connect Laravel query builder syntax to the actual SQL being sent to the database.

Take Quiz

Real Codebase Usage

In real Laravel projects, developers usually inspect queries using a few common patterns.

1. Inspect before execution

$query = DB::table('users')->where('role', 'admin');

dd($query->toSql(), $query->getBindings());

This is the simplest pattern when debugging one query.

2. Reuse the builder

$query = DB::table('users')->where('active', 1);

logger()->debug($query->toSql(), $query->getBindings());

$users = $query->get();

Store the builder in a variable so you can inspect it and then execute it.

3. Listen to all queries

Laravel can listen for every query executed during a request:

DB::listen(function ($query) {
    logger()->debug('SQL', [
        'sql' => $query->sql,
        'bindings' => $query->bindings,
        'time' => $query->time,
    ]);
});

This is useful when you want to inspect all database activity.

4. Use query logs in development

DB::enableQueryLog();

DB::table('users')->where('active', 1)->get();

$log = DB::getQueryLog();
dd($log);

This shows executed queries rather than just the builder template.

5. Guard against mixing debugging and execution

A common pattern is:

$query = DB::table('users');

if ($debug) {
    dd($query->toSql(), $query->getBindings());
}

return $query->get();

This keeps debugging separate from application behavior.

Common Mistakes

Calling toSql() after get()

Broken code:

DB::table('users')->get()->toSql();

Problem:

  • get() returns a collection of results
  • collections do not have the same query-builder methods

Fix:

DB::table('users')->toSql();

Expecting bound values inside toSql()

Broken expectation:

$query = DB::table('users')->where('id', 5);
echo $query->toSql();

You may expect:

select * from `users` where `id` = 5

But Laravel typically returns:

select * from `users` where `id` = ?

Fix: also inspect bindings.

$query = DB::table('users')->where('id', 5);
dd($query->toSql(), $query->getBindings());

Confusing executed SQL with builder SQL

  • toSql() shows the compiled query template
  • query logs and listeners show executed queries and metadata

Use the right tool for the right need.

Forgetting database-specific formatting

Different database drivers may format identifiers differently:

  • MySQL: backticks
  • PostgreSQL: double quotes
  • SQLite: may differ

So your output may not exactly match handwritten SQL, even though it is equivalent.

Trying to manually concatenate bindings unsafely

Some developers try to build a final SQL string manually for display. That can be misleading or unsafe if done carelessly.

If you only need debugging, prefer:

  • toSql()
  • getBindings()
  • DB::listen()
  • query logs

Comparisons

ToolWhat it returnsExecutes query?Best use
toSql()SQL template with ? placeholdersNoInspect query structure
getBindings()Bound parameter valuesNoInspect values sent with query
get()Result rowsYesFetch data
dd($query->toSql(), $query->getBindings())SQL + bindings dumpNoQuick debugging
DB::listen()Every executed query with time and bindingsYes, as part of app flowGlobal query debugging
DB::getQueryLog()Logged executed queriesYesReview queries after execution

Query builder vs Eloquent

ApproachExampleNotes
Query BuilderDB::table('users')->toSql()Works directly on the builder
EloquentUser::where('active', 1)->toSql()Eloquent query builders also support toSql()

Both follow the same idea: build first, inspect if needed, then execute.

Take Quiz

Cheat Sheet

// Get SQL template
$sql = DB::table('users')->toSql();

// Get bindings
$bindings = DB::table('users')->where('id', 1)->getBindings();

// Debug both
$query = DB::table('users')->where('id', 1);
dd($query->toSql(), $query->getBindings());

// Execute after inspection
$query = DB::table('users')->where('active', 1);
$users = $query->get();

// Listen to all queries
DB::listen(function ($query) {
    logger($query->sql, $query->bindings);
});

Rules to remember

  • Call toSql() on the query builder, not on the result of get()
  • toSql() usually returns placeholders like ?
  • Use getBindings() to see actual values
  • Use query logs or listeners to inspect executed queries
  • SQL formatting depends on the database driver

FAQ

How do I print the SQL query in Laravel?

Use toSql() on the query builder:

echo DB::table('users')->toSql();

Why does Laravel show ? instead of actual values?

Laravel uses prepared statements. The SQL and the values are stored separately for safety and performance.

How do I see the values for the placeholders?

Use getBindings():

$query = DB::table('users')->where('id', 10);
dd($query->toSql(), $query->getBindings());

Can I use toSql() with Eloquent?

Yes.

User::where('active', 1)->toSql();

Does toSql() run the query?

No. It only returns the SQL template.

How do I log all queries in Laravel?

Use DB::listen() or enable the query log with DB::enableQueryLog().

Why does my SQL use backticks or different quotes?

Laravel formats SQL based on the database driver you are using, such as MySQL or PostgreSQL.

Take Quiz

Mini Project

Description

Build a small Laravel debugging snippet that creates a filtered user query and inspects the generated SQL before executing it. This demonstrates the difference between the query builder itself, the SQL template, and the actual bindings.

Goal

Create and inspect a query using toSql() and getBindings(), then execute it to retrieve results.

Requirements

  1. Create a query for the users table.
  2. Add at least one where condition and one orderBy clause.
  3. Output the SQL template.
  4. Output the bindings.
  5. Execute the query and store the results.
Take Quiz

Keep learning

Related questions

View more