Hey there, SQL enthusiast! Ever felt the thrill of joining two tables and getting exactly the data you wanted? Or the panic when your query runs for hours and your database starts smoking?
I’ve been there too. Today, let’s talk about SQL joins - the good, the bad, and the “oh no, I just cross-joined two million-row tables.”
Think of joins like arranging a blind date between tables. You’re telling your database: “Hey, find all the users and match them with their orders… but only if they actually know each other (have matching IDs).”
-- The SQL equivalent of "you two should meet!"
SELECT users.name, orders.total
FROM users
INNER JOIN orders ON users.id = orders.user_id;
Only brings records that have matches in both tables. It’s that friend who only introduces people who actually want to meet.
SELECT employees.name, departments.department_name
FROM employees
INNER JOIN departments ON employees.dept_id = departments.id;
When to use: When you only care about perfect matches.
Brings all records from the left table, and matching records from the right. Null values fill in for no-shows.
SELECT customers.name, orders.order_date
FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id;
When to use: When you want all records from the main table, regardless of matches.
The same as LEFT JOIN, but from the other table’s perspective. So unpopular that many developers avoid it entirely and just use LEFT JOIN with reversed tables.
Brings all records from both tables, matching where possible. The ultimate FOMO join.
SELECT products.name, inventory.quantity
FROM products
FULL JOIN inventory ON products.id = inventory.product_id;
When to use: When you need to see everything, matches or not.
Matches every row from the first table with every row from the second. Handle with care!
-- Creates a massive grid of all combinations
SELECT sizes.size, colors.color
FROM sizes
CROSS JOIN colors;
When to use: Only with small tables, or when you’re absolutely sure you want billions of rows.
We’ve all been the developer who took down the staging database. Here’s how to avoid it:
-- Friendly reminder: this will create 1,000,000 x 1,000,000 = 1 trillion rows
SELECT *
FROM huge_customers_table -- 1,000,000 rows
CROSS JOIN massive_orders_table; -- 1,000,000 rows
Moral of the story: Cross joins are like all-you-can-eat buffets - great in theory, dangerous in practice.
Imagine trying to find someone in a crowded room without knowing their name. That’s your database without indexes.
-- Without an index on user_id, this is slooooow
SELECT *
FROM users
JOIN orders ON users.id = orders.user_id;
Pro tip: Index your foreign keys. Your database will thank you.
-- This makes your database work overtime
SELECT *
FROM products
JOIN sales ON UPPER(products.name) = UPPER(sales.product_name);
Better to clean your data first, then join.
-- Good: Clear what you're doing
SELECT users.name
FROM users
INNER JOIN orders ON users.id = orders.user_id;
-- Bad: Are you joining? Filtering? Who knows!
SELECT users.name
FROM users, orders
WHERE users.id = orders.user_id;
-- Clear and readable
SELECT u.name, o.total, p.product_name
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN products p ON o.product_id = p.id;
-- Please don't do this
SELECT a.n, b.t, c.pn
FROM users a
JOIN orders b ON a.i = b.ui
JOIN products c ON b.pi = c.i;
-- Better: Filter before joining
SELECT u.name, o.total
FROM (SELECT * FROM users WHERE active = true) u
JOIN (SELECT * FROM orders WHERE year = 2023) o ON u.id = o.user_id;
-- Worse: Join everything, then filter
SELECT u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.active = true AND o.year = 2023;
Before joining, ask:
-- Oops, forgot the WHERE clause!
SELECT *
FROM customers, orders;
-- Congratulations, you now have customers × orders rows!
-- Why am I missing data?
SELECT users.name, orders.total
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE orders.total > 100; -- This turns your LEFT JOIN into an INNER JOIN!
Fix:
SELECT users.name, orders.total
FROM users
LEFT JOIN orders ON users.id = orders.user_id AND orders.total > 100;
Before running that 10-table join on production:
-- Test with a sample first
SELECT *
FROM large_table lt
JOIN another_large_table alt ON lt.id = alt.lt_id
LIMIT 100;
Joins are powerful tools, but with great power comes great responsibility. Remember:
Now go forth and join responsibly! Your database (and your fellow developers) will thank you.
Got any join horror stories or pro tips? Share them with me
Enjoyed this article? Subscribe to get notified when I post about window functions, CTEs, and other SQL magic!
If this made you nod, laugh, or have butterflies in the stomach or elsewhere — tell me about it.