Full Join in MySQL
Introduction
Complete datasets require preserving ALL records from both tables regardless of matches.
MySQL lacks FULL OUTER JOIN but simulates with UNION of LEFT + RIGHT JOIN.
It is widely used for:
- Complete data reconciliation
- Missing relationship analysis
- Full dataset reporting
What is Full Join
FULL OUTER JOIN returns ALL rows from both tables with matches where possible.
Non-matching rows show NULLs from missing side.
MySQL FULL JOIN Alternative
sql
SELECT s.name, c.course_name
FROM Students s
LEFT JOIN Courses c ON s.course_id = c.id
UNION
SELECT s.name, c.course_name
FROM Students s
RIGHT JOIN Courses c ON s.course_id = c.id
WHERE s.course_id IS NULL;
Example
sql
-- All students + all courses (NULL where no match)
SELECT s.id as student_id, s.name, c.id as course_id, c.course_name
FROM Students s
LEFT JOIN Courses c ON s.course_id = c.id
UNION
SELECT s.id as student_id, s.name, c.id as course_id, c.course_name
FROM Students s
RIGHT JOIN Courses c ON s.course_id = c.id
WHERE s.id IS NULL;
Simplified UNION Syntax
Cleaner column matching.
sql
SELECT s.name, c.course_name
FROM Students s LEFT JOIN Courses c ON s.course_id = c.id
UNION ALL
SELECT s.name, c.course_name
FROM Courses c LEFT JOIN Students s ON s.course_id = c.id
WHERE s.course_id IS NULL;
Full Join with WHERE
Filter after complete dataset.
sql
SELECT s.name, c.course_name
FROM Students s LEFT JOIN Courses c ON s.course_id = c.id
UNION
SELECT s.name, c.course_name
FROM Students s RIGHT JOIN Courses c ON s.course_id = c.id
WHERE s.course_id IS NULL
HAVING s.name IS NOT NULL OR c.course_name IS NOT NULL;
Full Join with ORDER BY
Sort complete dataset.
sql
SELECT COALESCE(s.name, 'No Student') as student,
COALESCE(c.course_name, 'No Course') as course
FROM Students s LEFT JOIN Courses c ON s.course_id = c.id
UNION
SELECT COALESCE(s.name, 'No Student'),
COALESCE(c.course_name, 'No Course')
FROM Students s RIGHT JOIN Courses c ON s.course_id = c.id
WHERE s.course_id IS NULL
ORDER BY student, course;
Full Join with GROUP BY
Aggregate across complete set.
sql
SELECT COALESCE(s.city, 'Unknown') as city, COUNT(*) as total_records
FROM Students s LEFT JOIN Courses c ON s.course_id = c.id
UNION
SELECT COALESCE(s.city, 'Unknown'), COUNT(*)
FROM Students s RIGHT JOIN Courses c ON s.course_id = c.id
WHERE s.course_id IS NULL
GROUP BY COALESCE(s.city, 'Unknown');
Identifying Match Types
Use NULLs to categorize relationships.
sql
SELECT
s.name,
c.course_name,
CASE
WHEN s.name IS NULL THEN 'Course Only'
WHEN c.course_name IS NULL THEN 'Student Only'
ELSE 'Matched'
END as relationship
FROM Students s LEFT JOIN Courses c ON s.course_id = c.id
UNION
SELECT s.name, c.course_name, relationship
FROM Students s RIGHT JOIN Courses c ON s.course_id = c.id
WHERE s.course_id IS NULL;
Performance Comparison
| Method | Speed | Memory | Complexity |
| FULL JOIN (UNION) | Medium | High | Complex |
| INNER JOIN | Fastest | Low | Simple |
| LEFT JOIN | Fast | Medium | Simple |
Full Join Multiple Tables
Complex complete datasets.
sql
-- Students + Courses + Departments (all combinations preserved)
SELECT s.name, c.course_name, d.dept_name
FROM Students s
LEFT JOIN Courses c ON s.course_id = c.id
LEFT JOIN Departments d ON c.dept_id = d.id
UNION
-- Additional RIGHT JOIN logic for complete coverage
SELECT s.name, c.course_name, d.dept_name
FROM Departments d
LEFT JOIN Courses c ON d.id = c.dept_id
LEFT JOIN Students s ON c.id = s.course_id
WHERE s.course_id IS NULL OR c.dept_id IS NULL;
Execution Order with Full Join
LEFT JOIN → RIGHT JOIN → UNION → WHERE → GROUP BY → ORDER BY → LIMIT
Performance Considerations
- UNION removes duplicates (use UNION ALL if duplicates OK)
- Index all join columns
- Large tables: consider separate LEFT/RIGHT queries
- COALESCE improves NULL readability
Important Notes
- MySQL lacks native FULL OUTER JOIN
- UNION LEFT + RIGHT = FULL JOIN
- Duplicate removal in UNION
- WHERE clauses affect completeness
- Complex but powerful for reconciliation
Example Scenario
Complete enrollment reconciliation report:
sql
SELECT
COALESCE(s.student_id, 'MISSING') as student_id,
COALESCE(s.name, 'No Student') as student_name,
COALESCE(c.course_id, 'MISSING') as course_id,
COALESCE(c.course_name, 'No Course') as course_name,
CASE
WHEN s.student_id IS NULL THEN 'Orphan Course'
WHEN c.course_id IS NULL THEN 'Unenrolled Student'
ELSE 'Enrolled'
END as status
FROM Students s LEFT JOIN Enrollments e ON s.student_id = e.student_id
LEFT JOIN Courses c ON e.course_id = c.course_id
UNION
SELECT student_id, student_name, course_id, course_name, status
FROM Courses c LEFT JOIN Enrollments e ON c.course_id = e.course_id
LEFT JOIN Students s ON e.student_id = s.student_id
WHERE s.student_id IS NULL OR e.course_id IS NULL
ORDER BY status, student_name, course_name;
Common Mistakes
- WHERE filtering before UNION completeness
- UNION ALL creating duplicates
- Non-indexed join columns
- Complex logic readability
- Performance expectations vs INNER JOIN
Key Points to Remember
- FULL JOIN = LEFT + RIGHT UNION
- Preserves ALL rows from both tables
- NULLs show missing relationships
- UNION removes duplicates
- Index join columns critical
- Use COALESCE for readable NULLs