Lesson 9: Joining Tables — The Art of SQL Matchmaking!


Lesson 9

Lesson 9: Joining Tables — The Art of SQL Matchmaking!

Welcome back, fellow database enthusiast! Today, we dive into the world of joins — SQL’s way of introducing tables to each other, like a savvy matchmaker who knows exactly which tables are meant to be together. Whether you’re pairing up employees with departments or heroes with their epic quests, mastering joins will make your PostgreSQL queries feel like a symphony of relationships.

Let’s start playing Cupid with your data!

What Is a Join?

A join in SQL is like setting up a blind date between two tables. You’re asking PostgreSQL to bring related data together, ensuring that rows from one table are matched up with rows from another, based on some shared value (like an ID, name, or maybe an obscure hobby… but probably just an ID).

There are several types of joins, each with its own “relationship dynamic” — from the harmonious inner join to the more complicated outer joins. Let’s meet the cast:

1. Inner Join — The Perfect Match

The inner join is the SQL equivalent of finding your perfect match on the first date. It only returns rows where both tables have matching values in the columns you specify. If there’s no match, those rows are left out in the cold.

Think of it as: “You two have something in common? Great, let’s bring you together!”

SELECT employees.name, departments.department_name
FROM employees
INNER JOIN departments
ON employees.department_id = departments.id;

In this query, PostgreSQL will only show you employees that have a matching department. No department? No table for you!

2. Left Join (or Left Outer Join) — The Generous Friend

The left join is like that friend who always makes sure no one is left behind. It returns all the rows from the left table (the one you list first), even if there’s no matching row in the right table. Where there’s no match, PostgreSQL kindly fills in the blanks with NULL.

In short: “I don’t care if the right table is busy — I’ll still take all the left table’s rows and give them a chance!”

SELECT heroes.name, quests.quest_name
FROM heroes
LEFT JOIN quests
ON heroes.quest_id = quests.id;

With this query, even if a hero hasn’t been assigned a quest yet, they’ll still show up, just with a blank space where the quest name should be (cue Taylor Swift).

3. Right Join (or Right Outer Join) — The Supportive Backup

The right join is like the left join’s sibling, but with a twist. It returns all rows from the right table, even if there’s no match in the left table. So, if the right table has extra rows that don’t align with the left table, they’re still included.

It’s like saying: “Hey, right table, we got your back, no matter what.”

SELECT quests.quest_name, heroes.name
FROM quests
RIGHT JOIN heroes
ON quests.id = heroes.quest_id;

Here, if a quest hasn’t been assigned a hero, it’ll still appear. Maybe it’s waiting for the next brave soul to come along!

4. Full Outer Join — The Inclusive One

The full outer join is SQL’s version of an open invitation. It returns all rows when there is a match in either table, and when there isn’t, PostgreSQL just shrugs and fills in NULLs. Everyone gets to come to the party, whether they have a match or not.

It’s like saying: “You all get in! Whether you have a match or not, we’ll make space for you.”

SELECT heroes.name, quests.quest_name
FROM heroes
FULL OUTER JOIN quests
ON heroes.quest_id = quests.id;

With this query, you get all the heroes and all the quests, even if some heroes are just sitting around waiting for adventure and some quests are desperately looking for a hero. The perfect recipe for a fantasy matchmaking agency!

Using JOIN with Multiple Tables

Why stop at two tables? Sometimes, SQL wants to play matchmaker for more than just a couple of tables. When you need to join three or more tables, PostgreSQL is more than happy to oblige. Just keep chaining your joins together!

SELECT heroes.name, quests.quest_name, kingdoms.kingdom_name
FROM heroes
JOIN quests ON heroes.quest_id = quests.id
JOIN kingdoms ON heroes.kingdom_id = kingdoms.id;

In this case, you’re combining heroes, their quests, and the kingdoms they belong to — like organizing a royal questing event with all the proper invitations!

Self Join — When Tables Get a Little Self-Centered

A self join is like SQL’s version of “talking to oneself.” You join a table with itself, typically to compare rows within the same table. Imagine a quest table where each quest has a follow-up quest — you can use a self join to see which quests are connected.

SELECT q1.quest_name AS original_quest, q2.quest_name AS followup_quest
FROM quests q1
JOIN quests q2 ON q1.followup_quest_id = q2.id;

Here, PostgreSQL will match each quest with its follow-up, creating a neat list of epic adventures that lead to more epic adventures!

Cross Join — Everyone’s Invited!

A cross join is the SQL equivalent of throwing everyone into a room and seeing what happens. It combines every row of the left table with every row of the right table — and the result can be… a lot. You end up with every possible combination, which can be useful but is often chaotic.

SELECT heroes.name, weapons.weapon_name
FROM heroes
CROSS JOIN weapons;

Now you have every hero wielding every possible weapon — a warrior’s fantasy, or possibly a database overload!

What Did We Learn Today?

Today we learned how to bring tables together and create harmonious relationships through:

  • Inner joins: Where only the perfect matches survive.
  • Left and right joins: Because sometimes it’s okay to play favorites.
  • Full outer joins: For when you want to include everyone, whether they fit or not.
  • Self joins: When tables need to have a little conversation with themselves.
  • Cross joins: Because chaos is sometimes fun!

What’s Next?

In the next lesson, we’ll dive into Window Functions — a fancy way to calculate running totals, rankings, and more! Get ready to give your queries an upgrade worthy of a royal treatment.


Now go forth, and may your joins always bring the right tables together!