Hãy tưởng tượng mỗi bảng trong database của bạn là một mảnh ghép của một bức tranh lớn. Bảng students biết ai đang học, bảng courses biết học cái gì, còn bảng enrollments biết ai đăng ký cái gì. Nhưng nếu tách riêng thì mấy mảnh này chẳng nói lên điều gì cả. Muốn nhìn thấy toàn bộ bức tranh? Phải ghép chúng lại — và đây là lúc nhiều JOIN xuất hiện.
Trong thực tế, dữ liệu thường được tổ chức thành các bảng liên kết để đảm bảo cấu trúc và tránh dư thừa. Ví dụ, trong database trường đại học của tụi mình có các bảng sau:
students— dữ liệu về sinh viên.enrollments— thông tin về việc sinh viên đăng ký khóa học.courses— thông tin về các khóa học.
Nếu muốn lấy danh sách đầy đủ sinh viên, các khóa học của họ và giáo viên, thì phải kết hợp ba bảng này bằng JOIN.
Thứ tự thực hiện JOIN
Khi dùng nhiều JOIN, PostgreSQL xử lý từ trái sang phải. Nghĩa là nó kết hợp hai bảng đầu tiên trước, sau đó kết quả đó lại kết hợp với bảng thứ ba, cứ thế tiếp tục.
Ví dụ:
SELECT *
FROM students
INNER JOIN enrollments ON students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id;
- Đầu tiên kết hợp bảng
studentsvàenrollmentstheo cộtstudents.id = enrollments.student_id. - Kết quả của lần kết hợp đầu sẽ dùng để kết hợp tiếp với bảng
coursestheo cộtenrollments.course_id = courses.id.
Thứ tự thực hiện rất quan trọng, nhất là khi truy vấn trên bảng lớn. Cấu trúc JOIN không hợp lý có thể làm giảm hiệu năng đáng kể.
Ví dụ: Danh sách sinh viên, khóa học và giáo viên
Giả sử tụi mình có các bảng dữ liệu như sau:
Bảng students:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
Bảng courses:
| id | name | teacher |
|---|---|---|
| 101 | Mathematics | Ellen Moore |
| 102 | Physics | James Okoro |
| 103 | Computer Science | Nina Delgado |
Bảng enrollments:
| student_id | course_id |
|---|---|
| 1 | 101 |
| 1 | 103 |
| 2 | 102 |
| 3 | 101 |
Truy vấn:
SELECT
students.name AS student_name,
courses.name AS course_name,
courses.teacher AS teacher_name
FROM students
INNER JOIN enrollments ON students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id;
Kết quả:
| student_name | course_name | teacher_name |
|---|---|---|
| Otto Song | Mathematics | Ellen Moore |
| Otto Song | Computer Science | Nina Delgado |
| Maria Chi | Physics | James Okoro |
| Alex Lin | Mathematics | Ellen Moore |
Lọc dữ liệu trong truy vấn với nhiều JOIN
Bạn có thể áp dụng điều kiện lọc cho truy vấn với JOIN để giới hạn dữ liệu trả về và tăng tốc độ truy vấn. Ví dụ, chỉ lấy sinh viên học môn "Mathematics":
SELECT
students.name AS student_name,
courses.name AS course_name
FROM students
INNER JOIN enrollments ON students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id
WHERE courses.name = 'Mathematics';
Kết quả:
| student_name | course_name |
|---|---|
| Otto Song | Mathematics |
| Alex Lin | Mathematics |
Tối ưu hóa truy vấn với nhiều JOIN
Khi làm việc với bảng lớn, tối ưu hóa truy vấn là cực kỳ quan trọng. Đây là vài mẹo nhỏ:
- Dùng index
Index giúp PostgreSQL chạy nhanh hơn, nhất là khi JOIN theo các trường khóa. Hãy chắc chắn là cột student_id và course_id trong bảng enrollments có index nhé.
Ví dụ tạo index:
CREATE INDEX idx_enrollments_student_id ON enrollments(student_id);
CREATE INDEX idx_enrollments_course_id ON enrollments(course_id);
Chi tiết về index sẽ học ở level sau, nhưng mình nhắc ở đây vì nó rất hay đi kèm với JOIN.
- Lọc dữ liệu càng sớm càng tốt
Dùng điều kiện WHERE để giảm số dòng xử lý trước khi JOIN. Ví dụ:
SELECT
students.name AS student_name,
courses.name AS course_name
FROM students
INNER JOIN enrollments ON students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id
WHERE
courses.teacher = 'Ivan Petrov';
- Giảm số dòng cần JOIN
Thay vì JOIN tất cả dòng của hai bảng, hãy thử lọc trước bằng subquery:
SELECT
students.name AS student_name,
courses.name AS course_name
FROM
(SELECT * FROM students WHERE id IN (1, 2)) sub_students
INNER JOIN enrollments ON sub_students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id;
Chi tiết về SELECT lồng nhau sẽ học ở level tiếp theo :P
Ví dụ JOIN phức tạp: sinh viên, khóa học và khoa
Giả sử tụi mình có thêm bảng faculties:
Bảng faculties:
| id | name |
|---|---|
| 10 | Engineering |
| 20 | Natural Sciences |
Bảng courses cập nhật:
| id | name | teacher | faculty_id |
|---|---|---|---|
| 101 | Mathematics | Ellen Moore | 10 |
| 102 | Physics | James Okoro | 20 |
| 103 | Computer Science | Nina Delgado | 10 |
Để lấy danh sách sinh viên, khóa học và khoa, tụi mình thêm một JOIN nữa:
SELECT
students.name AS student_name,
courses.name AS course_name,
faculties.name AS faculty_name
FROM students
INNER JOIN enrollments ON students.id = enrollments.student_id
INNER JOIN courses ON enrollments.course_id = courses.id
INNER JOIN faculties ON courses.faculty_id = faculties.id;
Kết quả:
| student_name | course_name | faculty_name |
|---|---|---|
| Otto Song | Mathematics | Engineering |
| Otto Song | Computer Science | Engineering |
| Maria Chi | Physics | Natural Sciences |
| Alex Lin | Mathematics | Engineering |
SQL với nhiều JOIN có thể hơi phức tạp, nhưng nó giúp bạn tạo ra báo cáo mạnh mẽ và lấy được thông tin giá trị. Nếu tối ưu và cấu trúc đúng, nó sẽ là công cụ xịn xò cho bạn khi làm việc với database lớn.
GO TO FULL VERSION