Real databases almost NEVER keep all data in one giant table โ instead, related data is split across MULTIPLE tables (like Employees in one table, Departments in another). This chapter teaches you how to combine tables (JOINS) and combine query results (SET Operations) to get the full picture.
Focus especially on Equi Join vs Natural Join and UNION vs INTERSECT vs MINUS โ revise these three comparisons carefully, and type every query yourself.
8.1 ๐ Introduction
graph LR
JOIN["๐ JOIN\nCombines TABLES\n(columns from 2+ tables\ninto one result)"]
SET["๐งฎ SET Operations\nCombines QUERY RESULTS\n(rows from 2+ SELECTs\ninto one result)"]
style JOIN fill:#2196F3,color:#fff
style SET fill:#9C27B0,color:#fff
Analogy: Imagine your school keeps Student Names in one register and Marks in another register โ both linked by Roll Number. A JOIN is like laying both registers side-by-side and matching rows by Roll Number. A SET Operation is like combining TWO different class lists (e.g., "Sports Team" list and "Music Club" list) to see who's in either, both, or only one!
JOIN
Combines COLUMNS from 2+ tables using a shared/related column
Employee table + Department table โ combined infoSET Operations
Combines ROWS from 2+ SELECT queries with the SAME columns
List of Dept IDs in EMP + List of Dept IDs in DEPTOur Two Sample Tables โ used throughout this entire chapter:
Table: EMP
| empno | ename | deptno | salary |
|---|---|---|---|
| 1 | Alam | 10 | 10300 |
| 2 | Srijeeta | 20 | 6220 |
| 3 | Bhaskar | 30 | 11320 |
| 4 | Emely | 10 | 20500 |
| 5 | Freddy | 30 | 11320 |
Table: DEPT
| deptno | dname | dhead |
|---|---|---|
| 10 | Sales | Ritika |
| 20 | HR | Ankit |
| 30 | Production | Abuzar |
| 40 | IT | Mesha |
Notice: deptno = 40 (IT) exists in DEPT but has NO employees in EMP. Keep this in mind โ it becomes important later!
8.2 ๐ Joining Tables
8.2.1 Unrestricted Join (Cartesian Product) ๐
The simplest (and messiest!) join โ combine EVERY row of Table 1 with EVERY row of Table 2, with NO condition at all.
-- cartesian_product.sql
SELECT empno, ename, dname
FROM emp, dept;
Output (Partial โ full output has 5 ร 4 = 20 rows!):
empno ename dname
1 Alam Sales
1 Alam HR
1 Alam Production
1 Alam IT
2 Srijeeta Sales
... ... ... (20 rows total)
graph LR
E["EMP\n5 rows"]
D["DEPT\n4 rows"]
CP["Cartesian Product\n5 ร 4 = 20 rows\n(Every combination!)"]
E --> CP
D --> CP
style CP fill:#F44336,color:#fff
Whenever you list multiple tables in FROM WITHOUT a matching condition, MySQL pairs EVERY row with EVERY row โ creating a huge, mostly meaningless result. This is almost always a MISTAKE unless you specifically want every combination!
"What is a Cartesian Product? How many rows result from joining a 5-row table with a 4-row table?" โ 2-mark question! Answer: A Cartesian Product results when tables are combined WITHOUT a join condition โ every row of the first table pairs with every row of the second. Rows = 5 ร 4 = 20 rows.
8.2.2 Restricted Join (Join) ๐
Add a WHERE condition to link the tables MEANINGFULLY โ only rows that actually MATCH are shown.
-- restricted_join.sql
SELECT empno, ename, dname
FROM emp, dept
WHERE emp.deptno = dept.deptno;
Output:
empno ename dname
1 Alam Sales
2 Srijeeta HR
3 Bhaskar Production
4 Emely Sales
5 Freddy Production
Notice: only 5 rows now (not 20!) โ because we ONLY kept rows where
emp.deptnomatchesdept.deptno. Also notice: 'IT' (deptno 40) is MISSING โ because no employee works in IT!
graph LR
E["EMP Table"]
D["DEPT Table"]
MATCH["WHERE emp.deptno = dept.deptno\n๐ Only MATCHING rows kept!"]
RESULT["โ
Meaningful Result\n(5 rows, IT excluded)"]
E --> MATCH
D --> MATCH
MATCH --> RESULT
style MATCH fill:#4CAF50,color:#fff
8.2.3 Using Table Aliases ๐ท๏ธ
An Alias is a short, temporary nickname for a table โ makes long queries shorter and clearer.
-- table_aliases.sql
SELECT e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;
Output:
ename dname
Alam Sales
Srijeeta HR
Bhaskar Production
Emely Sales
Freddy Production
eis now the nickname foremp, anddis the nickname fordept. Instead of typingemp.deptno, we just typee.deptnoโ shorter and cleaner!
The alias e and d only exist for the DURATION of this one query. The actual table names (emp, dept) in the database are completely unchanged.
8.2.4 Additional Search Conditions in Joins ๐ฏ
You can combine the JOIN condition with EXTRA filter conditions using AND.
-- additional_conditions.sql
SELECT e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND d.dname = 'HR';
Output:
ename dname
Srijeeta HR
The Logic:
WHERE e.deptno = d.deptno โ Join condition (links the two tables)
AND d.dname = 'HR' โ Filter condition (narrows the result)
"Write a query to display employee names and salaries who work in the 'Production' department." โ Common 3-mark question! Answer:
SELECT e.ename, e.salary
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND d.dname = 'Production';8.2.5 Joining More Than Two Tables ๐๐๐
You're NOT limited to joining just 2 tables โ you can join 3, 4, or even more, as long as each pair has a linking condition!
Let's add a third table to demonstrate:
Table: PROJECT
| projno | projname | deptno |
|---|---|---|
| P1 | Website Redesign | 10 |
| P2 | Payroll System | 20 |
| P3 | Factory Automation | 30 |
-- joining_three_tables.sql
SELECT e.ename, d.dname, p.projname
FROM emp e, dept d, project p
WHERE e.deptno = d.deptno
AND d.deptno = p.deptno;
Output:
ename dname projname
Alam Sales Website Redesign
Srijeeta HR Payroll System
Bhaskar Production Factory Automation
Emely Sales Website Redesign
Freddy Production Factory Automation
graph LR
EMP["๐ EMP"]
DEPT["๐ DEPT"]
PROJ["๐ PROJECT"]
R["๐ Combined Result\n(Employee + Dept + Project\nall in ONE row!)"]
EMP -->|"e.deptno = d.deptno"| R
DEPT -->|"d.deptno = p.deptno"| R
PROJ --> R
style R fill:#4CAF50,color:#fff
Key Rule: When joining 3+ tables, you need (n-1) join conditions for
ntables. Joining 3 tables needs 2 conditions; joining 4 tables needs 3 conditions โ all connected withAND!
"How many join conditions are needed to join 4 tables?" โ 1-mark question!
Answer: 3 conditions (in general, joining n tables needs n-1 conditions to properly link them all).
8.2.6 Equi Join โ๏ธ
An Equi Join uses the equality operator (=) to match rows โ and it's called "Equi" because of that = sign! When you use SELECT *, the common column appears TWICE (once from each table).
-- equi_join.sql
SELECT *
FROM emp, dept
WHERE emp.deptno = dept.deptno;
Output (Partial):
empno ename deptno salary deptno dname dhead
1 Alam 10 10300 10 Sales Ritika
2 Srijeeta 20 6220 20 HR Ankit
Notice:
deptnoappears TWICE โ once from EMP, once from DEPT! This is the defining feature of an Equi Join withSELECT *.
"What is an Equi Join? Why does the common column appear twice?" โ 2-mark question!
Answer: An Equi Join links two tables using the equality (=) operator on a common column. When SELECT * is used, the joining column appears TWICE in the output โ once from EACH table โ because both columns are technically separate columns, even though their VALUES match.
8.2.7 Non-Equi Joins ๐ซ
A Non-Equi Join uses any operator OTHER than = โ like >, <, >=, <=, or != โ to link or filter rows.
-- non_equi_join.sql
SELECT e.ename, e.salary
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND e.salary > 15000;
Output:
ename salary
Emely 20500
The JOIN condition itself (
e.deptno = d.deptno) is still an Equi condition โ but the ADDITIONAL condition (e.salary > 15000) uses>, making this a Non-Equi filter combined with the join!
8.2.8 Natural Join ๐ฟ
A Natural Join automatically joins tables based on columns having the SAME NAME โ no need to write the condition yourself! And unlike Equi Join, the common column appears only ONCE.
-- natural_join.sql
SELECT ename, salary, dname
FROM emp NATURAL JOIN dept;
Output:
ename salary dname
Alam 10300 Sales
Srijeeta 6220 HR
Bhaskar 11320 Production
Emely 20500 Sales
Freddy 11320 Production
MySQL automatically found
deptnoas the common column name in BOTH tables and joined on it โ noWHEREneeded!
Equi Join vs Natural Join โ The Critical Comparison:
| Feature | Equi Join | Natural Join |
|---|---|---|
| Condition | You write it explicitly (WHERE table1.col = table2.col) |
MySQL finds it automatically (same column name) |
| Common column in output | Appears TWICE | Appears ONLY ONCE |
| Control | High โ you choose the column | Lower โ MySQL decides based on names |
| Risk | Safer, more predictable | Risky if tables share unexpected column names |
"Differentiate between Equi Join and Natural Join." โ 2-mark question.
Answer: Equi Join requires an explicit condition (WHERE t1.col = t2.col) and shows the joining column TWICE. Natural Join automatically joins on ALL columns with the same name (no WHERE needed) and shows the joining column only ONCE.
8.2.9 Joining Tables using JOIN Clause of SQL SELECT ๐
The MODERN, cleaner way to write joins โ using the explicit JOIN ... ON syntax instead of comma + WHERE.
-- modern_join_syntax.sql
SELECT ename, dname
FROM emp JOIN dept
ON emp.deptno = dept.deptno;
Output:
ename dname
Alam Sales
Srijeeta HR
Bhaskar Production
Emely Sales
Freddy Production
This produces the EXACT SAME result as the traditional comma + WHERE method โ just written differently. Both forms are accepted.
Old Style vs New Style โ Same Result, Different Syntax:
-- Old (traditional) style:
SELECT e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;
-- New (modern) style:
SELECT e.ename, d.dname
FROM emp e JOIN dept d
ON e.deptno = d.deptno;
Both are completely valid for CBSE board exams! The textbook usually teaches the comma + WHERE style first (which we've used throughout this chapter), but the JOIN...ON syntax is the modern industry standard. Learn both โ recognise both!
8.3 ๐งฎ Performing SET Operations on Relations
SET Operations combine the RESULTS of two or more SELECT queries โ treating them like mathematical sets!
Before you can combine two queries with UNION/MINUS/INTERSECT, they MUST have:
- The SAME number of columns
- Matching data types for corresponding columns
- Columns in the SAME order Breaking any of these rules causes an ERROR!
graph TD
Q1["Query 1: SELECT deptno FROM emp"]
Q2["Query 2: SELECT deptno FROM dept"]
RULE["Both must have:\nSame # of columns\nSame data types\nSame order"]
Q1 --> RULE
Q2 --> RULE
style RULE fill:#FF9800,color:#fff
8.3.1 UNION ๐
UNION combines the results of TWO queries into ONE list, automatically REMOVING duplicate rows.
-- union_demo.sql
SELECT deptno FROM emp
UNION
SELECT deptno FROM dept;
Output:
deptno
10
20
30
40
Why only 4 rows? Let's see what each query alone returns:
emp.deptno: 10, 20, 30, 10, 30 (5 rows, WITH duplicates)
dept.deptno: 10, 20, 30, 40 (4 rows)
UNION merges both AND removes duplicates:
โ 10, 20, 30, 40 (each value appears ONLY ONCE!)
graph LR
A["emp.deptno:\n10,20,30,10,30"]
B["dept.deptno:\n10,20,30,40"]
U["UNION\n(merge + remove duplicates)"]
R["Result:\n10, 20, 30, 40"]
A --> U
B --> U
U --> R
style U fill:#4CAF50,color:#fff
"What does the UNION operator do? Does it remove duplicates?" โ 2-mark question! Answer: UNION combines the result rows of two SELECT queries into a single result set, and it automatically removes duplicate rows.
8.3.2 MINUS Operator โ
MINUS shows records that are in the FIRST query's result but NOT in the second query's result โ like subtraction!
-- minus_demo.sql
SELECT deptno FROM dept
MINUS
SELECT deptno FROM emp;
Output:
deptno
40
Why just 40?
dept.deptno: 10, 20, 30, 40 (4 unique values)
emp.deptno: 10, 20, 30 (these appear in emp)
MINUS = "In dept but NOT in emp"
โ Only 40 (IT) remains โ because IT has no employees!
graph LR
D["DEPT deptnos:\n10, 20, 30, 40"]
E["EMP deptnos:\n10, 20, 30"]
M["MINUS\n(dept - emp)"]
R["Result: 40\n(IT โ no employees!)"]
D --> M
E --> M
M --> R
style M fill:#F44336,color:#fff
style R fill:#FF9800,color:#fff
A MINUS B is DIFFERENT from B MINUS A!
dept MINUS emp โ finds departments with NO employees (40)
emp MINUS dept โ would find employee deptnos not in dept table (none, in our case โ since every employee's dept exists in DEPT)
Always check WHICH table comes first!
8.3.3 INTERSECT Operator โ๏ธ
INTERSECT shows only the records that appear in BOTH queries โ the COMMON values.
-- intersect_demo.sql
SELECT deptno FROM emp
INTERSECT
SELECT deptno FROM dept;
Output:
deptno
10
20
30
Why these three?
emp.deptno: 10, 20, 30 (unique values in emp)
dept.deptno: 10, 20, 30, 40
INTERSECT = "values present in BOTH lists"
โ 10, 20, 30 (all three exist in both!)
โ 40 is EXCLUDED (only in dept, not in emp)
graph TD
E["EMP: 10, 20, 30"]
D["DEPT: 10, 20, 30, 40"]
I["INTERSECT\n(common values only)"]
R["Result: 10, 20, 30"]
E --> I
D --> I
I --> R
style I fill:#9C27B0,color:#fff
style R fill:#4CAF50,color:#fff
๐ UNION vs MINUS vs INTERSECT โ The Ultimate Visual Comparison
graph TD
Q1["Query 1 Result"]
Q2["Query 2 Result"]
UNION["UNION\n= Everything from BOTH\n(no duplicates)"]
MINUS["MINUS\n= In Query1 ONLY\n(not in Query2)"]
INTERSECT["INTERSECT\n= Only what's COMMON\nto BOTH"]
Q1 --> UNION
Q2 --> UNION
Q1 --> MINUS
Q2 --> MINUS
Q1 --> INTERSECT
Q2 --> INTERSECT
style UNION fill:#4CAF50,color:#fff
style MINUS fill:#F44336,color:#fff
style INTERSECT fill:#9C27B0,color:#fff
Think of it like a Venn Diagram!
Query 1 Query 2
โโโโโโโโโ โโโโโโโโโ
โ A โโโโโโโบโ B โ
โโโโโโโโโ โโโโโโโโโ
UNION = A + B (everything, no repeats)
MINUS = A - B (only in A, remove anything also in B)
INTERSECT = A โฉ B (only the overlapping middle part)
| Operator | Shows | Using our example |
|---|---|---|
| UNION | Everything from both queries (duplicates removed) | 10, 20, 30, 40 |
| MINUS | In first query, but NOT in second | 40 (dept MINUS emp) |
| INTERSECT | Only what's common to BOTH | 10, 20, 30 |
"Differentiate between UNION, MINUS, and INTERSECT with examples." โ 3-mark question, asked constantly! UNION: Combines all rows from both queries, removing duplicates. MINUS: Shows rows from the FIRST query that do NOT appear in the second. INTERSECT: Shows only rows that appear in BOTH queries.
โ ๏ธ Common Errors and Misconceptions
| Mistake | What Goes Wrong | Correct Understanding |
|---|---|---|
| โ Joining tables without a WHERE condition | Produces a massive, meaningless Cartesian Product | โ Always add a matching condition (or use JOIN...ON) |
| โ Thinking Equi Join and Natural Join give identical output | Equi Join shows common column TWICE; Natural Join shows it ONCE | โ Natural Join removes the duplicate column automatically |
โ A MINUS B = B MINUS A |
Order matters! Different results! | โ MINUS is NOT symmetric โ always check which table is "first" |
| โ Using UNION when columns don't match in number/type | Causes an ERROR | โ Both queries MUST have same number of columns, same types, same order |
| โ Thinking UNION keeps duplicates | UNION removes them automatically | โ
Use UNION ALL (not covered here) if you WANT duplicates kept |
| โ Forgetting IT (deptno 40) has no employees | Assuming Restricted Join shows ALL departments | โ Restricted/Equi Join only shows MATCHING rows โ unmatched rows (like IT) are excluded |
Joins โ One-Line Summary:
| Join Type | Key Idea |
|---|---|
| Cartesian (Unrestricted) | No condition โ every row ร every row |
| Restricted (Join) | Has a WHERE condition โ only matching rows |
| Equi Join | Uses =; common column appears TWICE |
| Non-Equi Join | Uses >, <, etc. instead of = |
| Natural Join | Auto-joins on same column name; appears ONCE |
| JOIN...ON | Modern syntax; same result as comma+WHERE |
SET Operations โ One-Line Summary:
| Operator | What it Shows |
|---|---|
| UNION | Everything from both (no duplicates) |
| MINUS | In first query ONLY, not in second |
| INTERSECT | Common to BOTH queries |
SET Operation Golden Rules:
- Same number of columns โ
- Same data types โ
- Same column order โ
๐ฏ Sample Board Exam Questions
Q1: Very Short Answer [1 mark each]
a) What is a Cartesian Product? โ The result of joining tables WITHOUT a matching condition โ every row of Table 1 pairs with every row of Table 2
b) Which operator shows rows common to two queries? โ INTERSECT
c) In an Equi Join using SELECT *, how many times does the common column appear?
โ Twice
d) Which SQL keyword automatically joins tables based on matching column names? โ NATURAL JOIN
e) If Table A has 6 rows and Table B has 3 rows, how many rows result from their Cartesian Product? โ 18 (6 ร 3)
Q2: Short Answer [2 marks]
Q: Differentiate between Equi Join and Natural Join.
Equi Join requires you to explicitly write the join condition using = (e.g., WHERE emp.deptno = dept.deptno), and when using SELECT *, the joining column appears TWICE in the result.
Natural Join automatically detects and joins on columns with the SAME NAME in both tables โ no condition needs to be written, and the common column appears only ONCE.
Q3: Query Writing [3 marks]
Q: Using the EMP and DEPT tables, write queries to:
a) Display employee names and their department names.
SELECT e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;
b) Display employees earning more than 10000, along with department name.
SELECT e.ename, e.salary, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno
AND e.salary > 10000;
c) Display department numbers that exist in DEPT but have no employees.
SELECT deptno FROM dept
MINUS
SELECT deptno FROM emp;
Q4: Output Based [3 marks]
Q: Based on the EMP and DEPT tables, what is the output of:
SELECT deptno FROM emp
INTERSECT
SELECT deptno FROM dept;
Output:
deptno
10
20
30
(These 3 department numbers exist in BOTH tables; 40 is excluded since it only exists in DEPT)
Q5: Differentiate [2 marks]
Q: What is the difference between UNION and INTERSECT?
UNION combines ALL rows from both queries into one list, removing duplicates โ it shows what exists in EITHER query. INTERSECT shows only the rows that exist in BOTH queries simultaneously โ the overlapping/common values.
โ๏ธ Practice Problems
-
Using EMP and DEPT, write a query to display all employee names with their department head's name (dhead).
-
Write a query using Natural Join to display employee name, salary, and department name.
-
Write a query to find department numbers that have employees earning more than 11000 (using a Non-Equi condition combined with the join).
-
Explain, with row counts, why
SELECT * FROM emp, dept;returns 20 rows whileSELECT * FROM emp, dept WHERE emp.deptno = dept.deptno;returns only 5. -
Write a query using UNION to combine all department numbers that appear in EITHER the EMP or DEPT table.
-
Write a query using MINUS to find employee department numbers that do NOT exist in the DEPT table (Hint: in our data, this returns nothing โ explain why).
-
What is the difference between the traditional comma+WHERE join syntax and the modern JOIN...ON syntax? Are their outputs different?
-
If Table A has 4 rows and Table B has 5 rows, how many rows would their Cartesian Product produce? Show the calculation.
-
Write a query joining THREE tables: EMP, DEPT, and a new PROJECT table (linked via deptno), to display employee name, department name, and project name.
-
A table has columns (Name, Marks) and another has (Name, Marks, Grade). Can you perform a UNION between them directly? Explain why or why not, referencing the Golden Rules of SET Operations.