Tech AI Insights

Laravel: 30 Essential MySQL Query Builder Examples

In this blog,we explore how Laravel Eloquent and Query Builder make database interactions seamless. Whether you’re fetching data, filtering records, or performing complex joins, MySQL functions play a crucial role in optimizing your Laravel applications.

We’ll cover essential functions like Functions for Conditions, Functions for Date/Time, Functions for Strings, Functions for JSON —all with practical Laravel examples. If you’re a web developer looking to enhance your database skills, this guide will help you write efficient queries, improve performance, and make the most of MySQL in Laravel. Stay ahead in the world of web development with these must-know techniques!

Functions for Conditions

IN (Checking Against a List)

IN checks if a value matches any item in a list or group, like a quick way to compare.

User::whereIn('country_id', [33,43,55,74,78,83,84,90])->where('is_admin', 0)->pluck('id')->toArray();

Get the IDs of users who are not admins and belong to specific countries.

whereHas& whereNotIn (Excluding Values from a List)

whereHas filters results based on a related model, while code>whereNotIn excludes results matching values in a list.

$users = User::whereHas('roles', function ($query) {
        $query->where('name', '!=', 'admin'); // Example condition for relation
    })
    ->where('status', 'active') // Example condition for filtering users
    ->whereNotIn('role', ['guest', 'banned', 'suspended']) // Excluding specific roles
    ->orderBy('role', 'ASC')
    ->get();

This query:

– Filters users who have a related roles record (excluding “admin”).
– Filters only users with status = 'active'.
– Excludes users with specific roles (guest, banned, suspended).
– Orders by role first, then by created_at.

COUNT() & BETWEEN (Filtering in a Range)

COUNT() counts items in a set, and BETWEEN filters values within a specified range.

$userCount = User::whereBetween('created_at', [Carbon::parse('2024-09-04'), Carbon::parse('2025-03-01')])
->where('role', '!=', 'Admin')
->count()

This query counts the number of users

– Filters users created between September 4, 2024, and March 1, 2025.
– Excludes users with the role “Admin”.

when (Conditional Queries)

When in conditional queries runs a condition and applies logic only if that condition is met.

$usersQuery = DB::table('users')
    ->select('id', 'name', 'email', 'country_id') // Selecting specific fields from the users table
    ->when($request->name, function ($query, $name) {
        return $query->where('name', 'LIKE', "%{$name}%"); // If a name is provided, filter users whose name contains the search term
    })
    ->when($request->email, function ($query, $email) {
        return $query->where('email', 'LIKE', "%{$email}%"); // If an email is provided, filter users whose email contains the search term
    })
    ->get()
    ->toArray();

This query:

Fetching Users: This query retrieves users from the users table.
Selecting Specific Fields: It only selects id, name, email, and country_id instead of fetching all columns.
Filtering by Name (Optional): If $request->name is present, it filters users whose name partially matches the given value.
Filtering by Email (Optional): If $request->email is present, it filters users whose email partially matches the given value.
Retrieving Data: Finally, it fetches the filtered list of users as an array.

IF (Conditional Queries)

IF in queries checks a condition and returns a value based on whether it’s true or false.

$query = User::query()
    ->select([
        'users.name',
        ('admin' || 'editor') ? 'users.role' : DB::raw('COUNT(DISTINCT users.role) as roles_count'),
        DB::raw('COUNT(users.id) as total_users'),
        DB::raw('SUM(IF(users.is_admin = 1, 1, 0)) as admin_count'),
        DB::raw('SUM(IF(users.role = "editor", 1, 0)) as editor_count'),
        DB::raw('SUM(IF(users.role = "subscriber", 1, 0)) as subscriber_count'),
        DB::raw('SUM(IF(users.is_active = 1, 1, 0)) as active_users'),
        DB::raw('SUM(IF(users.is_active = 0, 1, 0)) as inactive_users'),
    ])
    ->leftJoin('companies', 'companies.id', '=', 'users.company_id')
    ->whereHas('posts') // Assuming users have posts
    ->groupBy('users.name')
    ->get();

This query:

Conditional Column Selection → If 'admin' or 'editor' exists, show role; otherwise, count distinct roles.
Role-Based Summation → Counts total admins, editors, subscribers.
Active/Inactive Users → Counts users based on their is_active status.
Uses whereHas() → Ensures only users who have posts are included.
Uses groupBy() → Groups results by user name for better aggregation.

IFNULL (Conditional Queries)

IFNULL checks if a value is NULL; if so, it returns a default value instead.

$query = User::query()
    ->select([
        'users.id',
        'users.name',
        'users.email',
        DB::raw('IFNULL(users.phone, "No Phone Provided") as phone_number'),
        DB::raw('IFNULL(profiles.bio, "No Bio Available") as user_bio'),
        DB::raw('COUNT(posts.id) as total_posts'),
        DB::raw('SUM(IF(users.is_active = 1, 1, 0)) as active_users'),
        DB::raw('SUM(IF(users.is_verified = 1, 1, 0)) as verified_users'),
    ])
    ->leftJoin('profiles', 'profiles.user_id', '=', 'users.id')
    ->leftJoin('posts', 'posts.user_id', '=', 'users.id')
    ->groupBy('users.id')
    ->orderBy('users.name', 'asc')
    ->get();

This query:

Uses IFNULL() → If phone is NULL, replace it with "No Phone Provided".
Handles Profile Data → If bio is NULL, replace it with "No Bio Available".
Counts Related Posts → Calculates the total number of posts per user.
Checks Active & Verified Status → Counts how many users are active and verified.
Uses leftJoin() → Ensures profile and post data are included.
Groups Data by users.id → Prevents duplicate user rows.

CASE – (Complex Conditional Logic)

CASE is like an “if-else” for databases. It checks conditions and returns different values based on them.

User::select(
    'users.*', 
    'companies.country_id',
    DB::raw("IF(users.company_id IS NULL, NULL, IFNULL(companies.name, users.company_name)) as company_name"),
    DB::raw("(CASE 
                WHEN EXISTS (
                    SELECT 1
                    FROM user_restrictions
                    WHERE user_restrictions.pages = 'Users'
                    AND users.created_at BETWEEN user_restrictions.start_date AND user_restrictions.end_date
                )
                THEN TRUE
                ELSE FALSE
            END) as locked")
)
->leftJoin('companies', 'companies.id', '=', 'users.company_id')
->get()->toArray();

This query:

– Retrieves all users with their company’s country.
– Displays the correct company_name, handling null cases.
– Checks if a user is “locked” based on a user_restrictions table.
– Calculates salary_with_bonus by adding a percentage-based bonus to the user’s salary.

EXISTS – Check If a Subquery Returns Any Rows

EXISTS checks if a subquery returns any rows. If it does, the main query runs.

$query = User::query()
    ->select([
        'users.id',
        'users.name',
        'users.email',
        DB::raw('(EXISTS (
                    SELECT 1 
                    FROM orders 
                    WHERE orders.user_id = users.id 
                    LIMIT 1
                )) as has_orders'),
        DB::raw('COUNT(orders.id) as total_orders')
    ])
    ->leftJoin('orders', 'orders.user_id', '=', 'users.id')
    ->groupBy('users.id')
    ->orderBy('users.name', 'asc')
    ->get();

This query:

Uses EXISTS() → Checks if at least one order exists for the user.
Returns has_orders as true/false → Efficient way to check if a user has orders.
Counts Orders → Includes the total number of orders per user.
Uses leftJoin() → Ensures users without orders are still included.
Uses groupBy() → Prevents duplicate user rows.

NOT EXISTS – Check If No Rows Exist

NOT EXISTS checks if a subquery returns no rows, meaning the condition inside doesn’t match any data.

$query = User::query()
    ->select([
        'users.id',
        'users.name',
        'users.email',
        DB::raw('(NOT EXISTS (
                    SELECT 1 
                    FROM orders 
                    WHERE orders.user_id = users.id 
                    LIMIT 1
                )) as has_no_orders'),
        DB::raw('COUNT(orders.id) as total_orders')
    ])
    ->leftJoin('orders', 'orders.user_id', '=', 'users.id')
    ->groupBy('users.id')
    ->orderBy('users.name', 'asc')
    ->get();

This query:

Uses NOT EXISTS() → Returns true if no orders exist for a user.
Returns has_no_orders as true/false → Indicates whether a user has any orders.
Counts Total Orders → Even users with no orders are included.
Uses leftJoin() → Ensures users without orders remain in the result.
Uses groupBy() → Prevents duplicate user rows.

LEAST() – Get the Minimum Value Among Multiple Values

The LEAST() function returns the smallest value from a set of values you provide.

$query = User::query()
    ->select([
        'users.id',
        'users.name',
        'users.email',
        DB::raw('LEAST(users.age, users.experience_years, users.membership_duration) as min_value'),
        DB::raw('COUNT(posts.id) as total_posts'),
        DB::raw('IF(users.is_active = 1, "Active", "Inactive") as status')
    ])
    ->leftJoin('posts', 'posts.user_id', '=', 'users.id')
    ->groupBy('users.id')
    ->orderBy('min_value', 'asc')
    ->get();

This query:

Uses LEAST() → Finds the minimum value among age, experience_years, and membership_duration.
Includes User Info → Fetches name, email, and status.
Counts Posts → Calculates the total number of posts per user.
Uses IF() → Converts is_active into "Active" or "Inactive".
Sorts by min_value → Arranges users by their smallest value.

Functions for Date/Time

NOW() Get the Current Date & Time and CURDATE() Get the Current Date

$query = DB::table('users')
    ->select([
        'users.id',
        'users.name',
        'users.email',
        DB::raw('NOW() as current_datetime'),  -- Fetches current date and time
        DB::raw('CURDATE() as current_date'),  -- Fetches only the current date
        DB::raw('DATEDIFF(CURDATE(), users.created_at) as days_since_joined')  -- Calculates days since the user joined
    ])
    ->where('users.status', 'active')
    ->get();

This query:

NOW() → Fetches the full date and time (YYYY-MM-DD HH:MM:SS).
CURDATE() → Fetches only the date (YYYY-MM-DD).
(CURDATE(), users.created_at) → Calculates how many days a user has been registered.

This query helps track active users along with when they joined.

DATEDIFF() and TIMEDIFF()

You have a users table with created_at (account creation time) and last_login (last time they logged in). You want to:

1. Find the number of days since the user created the account (DATEDIFF).
2. Find the time difference between their last login and midnight of the same day (TIMEDIFF).

$query = User::query()
    ->select([
        'id',
        'name',
        'created_at',
        'last_login',
        DB::raw('DATEDIFF(NOW(), created_at) as days_since_signup'),
        DB::raw('TIMEDIFF(last_login, CURDATE()) as login_time_difference')
    ])
    ->orderBy('days_since_signup', 'DESC')
    ->get();

DATEDIFF(NOW(), created_at)</>→ Counts the number of days since the account was created.
TIMEDIFF(last_login, CURDATE())→ Calculates the time difference between last_login and midnight of the current date.
Sorting by days_since_signup→ Users who signed up the earliest appear first.

ADDDATE() – Add Days to a Date

Adds a specific number of days to a date.

SELECT ADDDATE('2025-03-27', INTERVAL 10 DAY); -- Output: 2025-04-06

SUBDATE() – Subtract Days from a Date

Subtracts a specific number of days from a date.

SELECT SUBDATE('2025-03-27', INTERVAL 5 DAY); -- Output: 2025-03-22

Functions for Strings

CONCAT() – Combine Strings

The CONCAT() function joins two or more strings together into one string.

$users = DB::table('users')
    ->select('*', DB::raw("GROUP_CONCAT(CONCAT('\"', name, '\"') SEPARATOR ', ') as <code>names<code>"))
    ->where([
        'status' => false,
        'deleted_at' => null
    ])
    ->groupBy('role_id')  // Assuming you want to group by <code>role_id<code>, change as needed
    ->get()
    ->toArray();

LENGTH() – Get String Length

The LENGTH() function in SQL returns the number of characters in a string, counting spaces and special characters.

$users = DB::table('users')
    ->select('id', 'name', DB::raw("LENGTH(name) as name_length"))
    ->get()
    ->toArray();

This query:

– Selects id and name from the users table.
– Uses LENGTH(name) to get the number of bytes in each user’s name.
– Returns the result as an array.

LOWER() / UPPER() – Change Case

$users = DB::table('users')
    ->select('id', 'email', 
        DB::raw("LOWER(name) as lowercase_name"), 
        DB::raw("UPPER(name) as uppercase_name")
    )
    ->get()
    ->toArray();

SUBSTRING() / SUBSTR() – Extract a Part of a String

SUBSTRING() or SUBSTR() extracts a specific portion from a string, starting at a given position for a certain length.

$users = DB::table('users')
    ->select('id', 'name', DB::raw("SUBSTRING(name, 1, 5) AS short_name"))
    ->get()

This query:

– Retrieves id and name from the users table.
– Uses SUBSTRING(name, 1, 5) to get the first 5 characters of each user’s name.
– Returns the result as short_name.

LOCATE() / POSITION() – Find Position of a Substring

$users = DB::table('users')
    ->select('id', 'name', DB::raw("LOCATE('a', name) as position"))
    ->get()
    ->toArray();

TRIM() – Remove Spaces

The TRIM() function removes extra spaces from the beginning and end of a text string.

$users = DB::table('users')
    ->select('id', DB::raw("TRIM(name) as trimmed_name"))
    ->get()
    ->toArray();

REPLACE() – Replace Part of a String

Replaces a substring within a string.

SELECT REPLACE('Hello World', 'World', 'MySQL') AS replaced;
-- Output: Hello MySQL

REVERSE() – Reverse a String

Flips the string backward.

SELECT REVERSE('Laravel') AS reversed;
-- Output: levaraL

FORMAT() – Format Number with Commas

Formats numbers with commas and decimal places.

SELECT FORMAT(1234567.89, 2) AS formatted;
-- Output: 1,234,567.89

FIELD() – Find Position in a List

The FIELD() function in SQL returns the position of a value in a list of values.

$users = DB::table('users')
    ->select('id', 'name', DB::raw("FIELD(role, 'admin', 'editor', 'subscriber') AS role_position"))
    ->orderBy('role_position')
    ->get();

This query:

1. FIELD(role, 'admin', 'editor', 'subscriber')
– This assigns a position based on the order in the list:
admin → Position 1
editor → Position 2
subscriber → Position 3
2. orderBy('role_position')
– Sorts users based on their role’s position.

Functions for JSON

JSON_OBJECT() – Create a JSON Object

Converts key-value pairs into a JSON object.

SELECT JSON_OBJECT(
    'id', users.id, 
    'name', users.name, 
    'email', users.email, 
    'role', users.role
) AS user_json
FROM users
WHERE users.id = 1;

Output

{
  "id": 1,
  "name": "jerry Damodar",
  "email": "john@example.com",
  "role": "admin"
}

JSON_ARRAY()/JSON_ARRAYAGG() – Create a JSON Array

Difference Between JSON_ARRAYAGG() and JSON_ARRAY()

json array

1 JSON_ARRAY(email) Returns a JSON Array for Each Row

If you use JSON_ARRAY(email), MySQL treats email as a single value and wraps it inside an array for each row.

SELECT id, name, JSON_ARRAY(email) AS user_emails FROM users;

Output:

json array

Each row gets a separate JSON array with only one email inside it

2 JSON_ARRAYAGG(email) Combines All Emails into One JSON Array

If you want one single JSON array containing all emails, you should use JSON_ARRAYAGG(email)

SELECT JSON_ARRAYAGG(email) AS user_emails FROM users;

Output (Single Row):

["madhav@example.com", "damodar@example.com", "govid@example.com"]

All emails are combined into one JSON array.

JSON_EXTRACT() – Get Value from JSON

Extracts values from a JSON column using path expressions.

$users = User::select('id', 'name', 
    DB::raw("JSON_EXTRACT(metadata, '$.preferences.theme') AS theme")
)->get();

Let’s suppose we store the data in the metadata column in the following JSON format:
like this

'{"preferences": {"theme": "dark", "language": "English"}}'

This query:

DB::raw("JSON_EXTRACT(metadata, '$.preferences.theme') AS theme")

– Uses raw SQL inside Laravel to run a MySQL JSON function.
JSON_EXTRACT(metadata, '$.preferences.theme'):

Extracts the value of the theme key inside the preferences object in the metadata JSON column.

AS theme: Renames the extracted value as theme in the output.

Output

[
    { "id": 1, "name": "Alice", "theme": "dark" },
    { "id": 2, "name": "Bob", "theme": "light" }
]

JSON_UNQUOTE() – Remove Quotes from JSON Output

removes double quotes from JSON-extracted values.

$users = User::select('id', 'name')
    ->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.preferences.theme')) LIKE ?", ['%dark%'])
    ->get();

You have a users table with a metadata column storing JSON data, and you want to find users where their preferred theme contains “dark”.

This query:

select('id', 'name') → Fetches only id and name, excluding other columns.
whereRaw(...) → Uses MySQL JSON functions inside WHERE:
JSON_EXTRACT(metadata, '$.preferences.theme') → Extracts theme value.
- JSON_UNQUOTE(...) → Removes double quotes/ from the JSON output.
- LIKE '%dark%' → Searches for any theme containing "dark" (e.g., "dark-mode", "dark-theme").

JSON_SET() – Update/Add a JSON Key-Value

Modifies a JSON field by setting a value at a given path.

Example: Update the user’s preferred language

UPDATE users
SET metadata = JSON_SET(metadata, '$.preferences.language', 'English')
WHERE id = 1;

Before:

{"preferences": {"theme": "dark"}}

After:

{"preferences": {"theme": "dark", "language": "English"}}

JSON_REMOVE() – Remove a Key from JSON

Deletes a key-value pair from a JSON field.

Example: Remove ‘language’ from user preferences


UPDATE users
SET metadata = JSON_REMOVE(metadata, '$.preferences.language')
WHERE id = 1;

Removes the language key from JSON data

JSON_CONTAINS() – Check If JSON Contains a Value

Checks if a JSON array contains a specific value.

Example: Check if a user has ‘dark mode’ enabled

SELECT JSON_CONTAINS(metadata, '"dark"', '$.preferences.theme') AS has_dark_mode
FROM users
WHERE id = 1;

Output: 1 (true) if "dark" exists.

JSON_LENGTH() – Count Elements in JSON

Counts the number of elements in a JSON object or array.

Example: Count the number of preferences a user has

SELECT JSON_LENGTH(metadata->'$.preferences') AS preference_count
FROM users
WHERE id = 1;

JSON_KEYS() – Get All Keys of a JSON Object

Returns an array of all keys inside a JSON object.

Example: Get all preference keys for a user

SELECT JSON_KEYS(metadata->'$.preferences') AS keys
FROM users
WHERE id = 1;

Output: ["theme", "language"]

JSON_MERGE_PRESERVE() – Merge Two JSON Objects

Combines JSON objects while keeping all values.

Example: Merge two JSON objects

SELECT JSON_MERGE_PRESERVE(
    '{"name": "John", "age": 30}',
    '{"city": "New York", "age": 32}'
) AS merged_json;

Output:

{"name": "John", "age": [30, 32], "city": "New York"}
Scroll to Top