All 40 lessons

Lesson 16 of 40 · Joins

SQL Joins and INNER JOIN

By Skillancy Editorial Team

Every example runs on the ShopKart dataset, already loaded in the free SQL Compiler. Click Try it in the SQL Compiler under a query to open it there, then press Run.

A join combines rows from two tables by matching values in a related column. ShopKart keeps orders in one table and customers in another. To show who placed each order, you join orders to customers on customer_id.

INNER JOIN keeps only rows that have a match in both tables.

Why data is split across tables

Storing the customer's name on every order would repeat it dozens of times, and changing it would mean updating every order. Instead, each order stores only customer_id, and the name lives once in customers. Joins put the pieces back together when you query. (This design is called normalization.)

Syntax

SELECT a.column, b.column
FROM table_a AS a
INNER JOIN table_b AS b
  ON a.key = b.key;

Types of joins at a glance

JoinReturns
INNER JOINonly rows that match in both tables
LEFT JOINall rows from the left table, matches from the right (or NULL)
RIGHT JOINall rows from the right table, matches from the left (or NULL)
FULL OUTER JOINall rows from both tables
CROSS JOINevery row paired with every row

This lesson covers INNER JOIN. The others follow in the next lessons.

Examples

1. Orders with customer names

SELECT o.order_id,
       o.order_date,
       c.customer_name,
       c.city
FROM orders AS o
INNER JOIN customers AS c
  ON o.customer_id = c.customer_id
ORDER BY o.order_id
LIMIT 5;
Try it in the SQL Compiler

Result:

order_idorder_datecustomer_namecity
10012025-01-05Aarav SharmaBengaluru
10022025-01-18Priya NairKochi
10032025-02-02Rohan GuptaDelhi
10042025-02-20Aarav SharmaBengaluru
10052025-03-11Sneha ReddyHyderabad

(5 rows)

For each order, SQL finds the customer row with the same customer_id and glues the two rows together.

2. Order lines with product names

SELECT oi.order_id,
       p.product_name,
       oi.quantity,
       oi.unit_price
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
ORDER BY oi.order_id
LIMIT 5;
Try it in the SQL Compiler

Result:

order_idproduct_namequantityunit_price
1001boAt Airdopes 14121299.00
1001Redmi Note 13 Pro123999.00
1002Women's Silk Saree18499.00
1003Steel Water Bottle 1L3549.00
1003Samsung 43" Smart TV131990.00

(5 rows)

JOIN on its own means INNER JOIN, and AS is optional for table aliases.

3. Join, then aggregate

Revenue by product name, the query we couldn't write in the GROUP BY lesson:

SELECT p.product_name,
       SUM(oi.quantity) AS units,
       ROUND(SUM(oi.quantity * oi.unit_price * (1 - oi.discount_pct / 100)), 2) AS net_revenue
FROM order_items oi
JOIN products p ON p.product_id = oi.product_id
GROUP BY p.product_name
ORDER BY net_revenue DESC
LIMIT 5;
Try it in the SQL Compiler

Result:

product_nameunitsnet_revenue
Redmi Note 13 Pro368397.15
Samsung 43" Smart TV255982.50
Women's Silk Saree324222.15
boAt Airdopes 14167794.00
Men's Cotton Kurta47501.05

(5 rows)

4. Join with a filter

Delivered orders from Prime members:

SELECT o.order_id, c.customer_name, o.order_date
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
WHERE c.is_prime
  AND o.status = 'Delivered'
ORDER BY o.order_date
LIMIT 6;
Try it in the SQL Compiler

Result:

order_idcustomer_nameorder_date
1001Aarav Sharma2025-01-05
1003Rohan Gupta2025-02-02
1005Sneha Reddy2025-03-11
1008Karan Mehta2025-05-03
1009Rohan Gupta2025-05-21
1011Aarav Sharma2025-06-30

(6 rows)

5. USING as a shortcut

When the join column has the same name in both tables:

SELECT order_id, customer_name
FROM orders
JOIN customers USING (customer_id)
LIMIT 3;
Try it in the SQL Compiler

Result:

order_idcustomer_name
1001Aarav Sharma
1002Priya Nair
1003Rohan Gupta

(3 rows)

INNER JOIN drops unmatched rows

ShopKart has 12 customers, but Divya Menon has never ordered. An inner join from customers to orders leaves her out:

SELECT COUNT(DISTINCT c.customer_id) AS customers_in_result
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;
Try it in the SQL Compiler

Result:

customers_in_result
11

(1 row)

That's correct for "customers who ordered" but wrong for "all customers and their order count". For that you need a LEFT JOIN.

Common mistakes

Ambiguous column names. When both tables have customer_id, say which one:

SELECT customer_id, customer_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;
Try it in the SQL Compiler

Error: column reference "customer_id" is ambiguous

Fix: SELECT o.customer_id, ....

  • Joining on the wrong column. ON o.order_id = c.customer_id runs without error and returns nonsense. Always join a foreign key to the key it references.
  • Forgetting that joins multiply rows. One order with three items becomes three rows after joining to order_items. Summing an order-level value afterwards double counts.

Practice question

List each order line with the customer's name and the product's name for order 1001. (Hint: you need three tables. Joining more than two is covered in lesson 20, but try it.)

Show solution
SELECT o.order_id, c.customer_name, p.product_name, oi.quantity
FROM orders o
JOIN customers c   ON c.customer_id = o.customer_id
JOIN order_items oi ON oi.order_id = o.order_id
JOIN products p    ON p.product_id = oi.product_id
WHERE o.order_id = 1001;
Try it in the SQL Compiler

Result:

order_idcustomer_nameproduct_namequantity
1001Aarav SharmaRedmi Note 13 Pro1
1001Aarav SharmaboAt Airdopes 1412

(2 rows)

Key takeaways

  • Joins combine tables by matching related columns.
  • INNER JOIN (or just JOIN) returns only matching rows.
  • Use table aliases and prefix columns that exist in both tables.
  • Joins can multiply rows; check counts before you sum.
Want every variation on one page?Joins in SQL: the complete guide

Frequently asked questions

What are the types of joins in SQL?

INNER JOIN (only matching rows), LEFT JOIN and RIGHT JOIN (all rows from one side plus matches), FULL OUTER JOIN (all rows from both sides) and CROSS JOIN (every combination). A self join is any of these with a table joined to itself.

Is JOIN the same as INNER JOIN?

Yes. JOIN on its own means INNER JOIN.

What is the difference between JOIN ON and JOIN USING?

ON lets you write any matching condition, such as o.customer_id = c.customer_id. USING (customer_id) is a shortcut when the column has the same name in both tables, and it shows that column only once in SELECT *.

Newsletter

Keep learning SQL

One email a week with SQL and analytics guides, new practice questions and cohort dates. No spam, unsubscribe anytime.

Prefer a one-page summary? Download the free SQL cheat sheet (PDF)