How to Optimize SQL Query with Multiple Joins

When trying to get information from a database quickly, it's important to optimize your SQL queries - especially when they involve multiple joins. Just like making a smoothie instead of eating all the fruits separately, a well-optimized SQL query blends data faster and more efficiently. Here, we'll show you some strategies to make your SQL queries as quick as a smoothie blend.

Start with the Basics – Understand Your Data

Like getting to know the ingredients of your smoothie before blending, you should understand your database. Make sure the fields used in your ON clauses have indices. Think of these as recipe tags that help in quickly finding and mixing your ingredients.

Be Clear - Use Explicit JOINs

Using explicit JOIN syntax instead of implicit join syntax (FROM table1, table2 WHERE...) is like listing out your recipe steps clearly. It improves both readability and speed.

For example, instead of writing a query like this:

SELECT * FROM Customers, Orders WHERE Customers.CustomerID = Orders.CustomerID;

You could use explicit JOIN syntax like this:

SELECT * FROM Customers JOIN Orders ON Customers.CustomerID = Orders.CustomerID;

Both queries do the same thing - list out all fields from Customers and Orders where the CustomerID matches - but the second query is easier to read and can be more efficient.

Analyze Your Queries - Use EXPLAIN

One important aspect of optimizing SQL queries is understanding how they are executed by the SQL engine. Tools like EXPLAIN provides insights into how your SQL query is being executed, allowing you to identify potential bottlenecks or inefficiencies.

For example, in MySQL, you can use `EXPLAIN` like so:

EXPLAIN SELECT * FROM Customers JOIN Orders ON Customers.CustomerID = Orders.CustomerID;

This will give you an overview of how MySQL interprets the query, displaying information about how tables are joined and in which order, how many rows it predicts it will need to process, which indexes it plans to use, and more.

In PostgreSQL, the usage is similar:

EXPLAIN SELECT * FROM Customers JOIN Orders ON Customers.CustomerID = Orders.CustomerID;

The result provides a detailed breakdown of the query plan, including the use of indexes, join strategies, and more.

It's important to note that the EXPLAIN plan doesn't execute the query, it just shows the plan. If you want to see the plan and run the query to get actual run-time statistics, you can use EXPLAIN ANALYZE in PostgreSQL or EXPLAIN with the FORMAT=JSON and ANALYZE option in MySQL.

Remember, interpreting EXPLAIN results can be complex and requires practice, but it is an essential tool in a performance-tuning toolbox. It is akin to having a detailed recipe with step-by-step instructions, letting you understand how your blender (database) will mix your ingredients for your smoothie (final result).

Try Some Substitutes - Use EXISTS Instead of IN

If you've used IN and NOT IN, try switching to EXISTS and NOT EXISTS.

For example, consider the following query which uses IN:

SELECT * FROM Orders WHERE CustomerID IN (SELECT CustomerID FROM Customers WHERE City = 'Berlin');

This query returns all orders placed by customers from Berlin.

You could rewrite that using EXISTS, which may often be more efficient:

SELECT o.* FROM Orders o WHERE EXISTS (SELECT 1 FROM Customers c WHERE c.City = 'Berlin' AND c.CustomerID = o.CustomerID);

This depends on the subquery results, if the subquery results are very large then use EXISTS. If the subquery results are very small then IN is still faster.

Be Selective – Avoid SELECT *

Choosing only the columns you need, instead of using SELECT *, cuts down on what your database has to process.

For example, instead of:

SELECT * FROM Customers JOIN Orders ON Customers.CustomerID = Orders.CustomerID;

You can use:

SELECT Customers.CustomerName, Orders.OrderID FROM Customers JOIN Orders ON Customers.CustomerID = Orders.CustomerID;

In this case, instead of selecting all fields, only the customer name and order ID are selected, thereby reducing the load on the database.

Less is More - Reduce Join Operations

Each join operation is like an extra blending action your database has to do. Try to keep them to a minimum and only blend in the necessary tables.

Consider, for example, a database containing four tables: Customers, Orders, Products, and Suppliers. If your goal is to get the list of all product names of the orders placed by customers from a specific city, you may be tempted to join all tables:

SELECT Products.ProductName
FROM ((Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID)
JOIN Products ON Orders.ProductID = Products.ProductID)
JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID
WHERE Customers.City = 'Berlin';

However, since we don't need any information from the Suppliers table for this task, the above query could be simplified and made more efficient by eliminating the unnecessary join:

SELECT Products.ProductName
FROM (Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID)
JOIN Products ON Orders.ProductID = Products.ProductID
WHERE Customers.City = 'Berlin';

Don't Forget the Keys - Indexing

Properly indexing your tables can significantly improve query performance, especially on larger datasets.

If we create an index on the `CustomerID` field in the Customers table, the SQL database can look up customers by their `CustomerID` at high speed. This will significantly speed up the execution of queries that involve searching for a customer by their `CustomerID`.

Create an index like this:

ON Customers (CustomerID);

After creating the index, you might want to check if the index has been created correctly. You can do this with a simple query, though the specific SQL command can differ slightly based on your SQL dialect. For example, in PostgreSQL, you could do:

\di idx_CustomerID

And in MySQL, you could use the following query to see information about your indexes:


In general, please check your SQL database's documentation for the correct query for viewing index information.

All in all, ensure you maintain good index habits like deleting unused ones and keeping them updated, which would be analogous to cleaning your blender and kitchen tools. This both helps in increasing performance and saving storage space.

Use LIMIT and OFFSET Judiciously

When dealing with large amounts of data, retrieving it all in one go is not always necessary, or optimal. We can use LIMIT to specify exactly how many rows to return, and OFFSET to specify where to start.

For example, to retrieve the first 10 orders placed by a certain customer, you could use a query like:

SELECT * FROM Orders WHERE CustomerID = '123' ORDER BY OrderDate LIMIT 10;

On the other hand, if you wanted to see the next 10 orders from the same customer, you would use an OFFSET:

SELECT * FROM Orders WHERE CustomerID = '123' ORDER BY OrderDate LIMIT 10 OFFSET 10;

Just keep in mind that while LIMIT and OFFSET can improve performance by reducing the amount of data returned in a query, they can also lead to performance issues if not used properly because OFFSET has to count off the number of rows to skip.


Optimizing SQL queries can significantly improve the performance of your applications, reducing loading times and making your user interfaces more responsive.

By taking the time to understand your data, choosing explicit JOIN syntax, using EXPLAIN, using EXISTS instead of IN, avoiding SELECT *, reducing the number of Join operations, creating indexes on your foreign keys, using LIMIT and OFFSET judiciously, you can create applications that can handle even the most data-intensive tasks quickly and efficiently.

Most of these are basic but it can make you a better developer and will result in a better end user experience. So, keep blending your queries until you find what tastes best for your application!

Start building your dashboard now

without coding