Bài 9: Truy vấn nhiều bảng (JOINs) – Sức mạnh của sự liên kết
I. Mục đích (Objectives)
Sau bài học này, người đọc sẽ:
- Hiểu bản chất của phép JOIN: Kết hợp các dòng từ hai hay nhiều bảng dựa trên một cột liên quan (thường là Khóa ngoại = Khóa chính).
- Phân biệt được hai loại JOIN phổ biến nhất: INNER JOIN và LEFT JOIN.
- Biết cách xử lý vấn đề “trùng tên cột” khi truy vấn nhiều bảng (Sử dụng Alias – bí danh).
- Viết được câu lệnh SQL để xuất ra báo cáo tổng hợp (Ví dụ: Danh sách học sinh kèm tên lớp và tên giáo viên).
II. Yêu cầu (Prerequisites)
- Đã có 2 bảng LopHoc và HocSinh có liên kết khóa ngoại (từ Bài 8: Khóa chính (Primary Key) và Khóa ngoại (Foreign Key) – Sợi dây kết nối dữ liệu).
- Đã có dữ liệu mẫu trong cả 2 bảng.
III. Nội dung chi tiết (Detailed Content)
1. Đặt vấn đề
Ở bài trước, trong bảng HocSinh, chúng ta chỉ lưu ma_lop_id là 1 hoặc 2. Khi người dùng nhìn vào số 1, họ không biết đó là lớp nào. Họ muốn nhìn thấy chữ “10A1”. Để làm được điều này, chúng ta cần “ghép” thông tin từ bảng LopHoc sang bảng HocSinh.
2. INNER JOIN (Kết nối nội) – “Có đôi có cặp”
Đây là kiểu Join phổ biến nhất. Nó chỉ lấy ra những dòng dữ liệu xuất hiện ở cả 2 bảng.
- Nguyên tắc: Chỉ những học sinh nào đã được xếp lớp VÀ lớp đó phải tồn tại thì mới hiện ra.
- Minh họa: Phần giao nhau của 2 vòng tròn (Biểu đồ Venn).
Cú pháp:
SELECT cot_can_lay
FROM Bang_A
INNER JOIN Bang_B ON Bang_A.khoa_ngoai = Bang_B.khoa_chinh;
Ví dụ thực hành: Lấy danh sách học sinh kèm tên lớp và giáo viên chủ nhiệm.
SELECT HocSinh.ho_ten, LopHoc.ten_lop, LopHoc.giao_vien_cn
FROM HocSinh
INNER JOIN LopHoc ON HocSinh.ma_lop_id = LopHoc.id_lop;
Giải thích:
- ON HocSinh.ma_lop_id = LopHoc.id_lop: Đây là điều kiện ghép cặp. Máy tính sẽ dò tìm: “Em này có mã lớp là 1, à bên bảng Lớp kia dòng id=1 là lớp 10A1, vậy ghép dòng đó vào đây”.
3. Giải quyết xung đột tên cột (Aliasing)
Giả sử cả 2 bảng đều có cột tên là id. Nếu bạn viết SELECT id …, máy tính sẽ báo lỗi “Ambiguous column name” (Tên cột mơ hồ) vì nó không biết bạn muốn lấy ID của học sinh hay ID của lớp.
Giải pháp: Gọi đích danh TenBang.TenCot. Để code ngắn gọn, ta dùng Alias (Bí danh – đặt tên tắt cho bảng).
-- Đặt tên tắt: h là HocSinh, l là LopHoc
SELECT h.ho_ten, l.ten_lop
FROM HocSinh AS h
INNER JOIN LopHoc AS l ON h.ma_lop_id = l.id_lop;
4. LEFT JOIN (Kết nối trái) – “Ưu tiên bảng chính”
Đôi khi, ta muốn liệt kê Tất cả học sinh, kể cả những em chưa được xếp lớp (mã lớp là NULL). Nếu dùng INNER JOIN, những em chưa có lớp sẽ bị ẩn đi. Lúc này ta dùng LEFT JOIN.
Nguyên tắc: Lấy tất cả dữ liệu bảng bên TRÁI (bảng viết sau chữ FROM), ghép với bảng bên PHẢI. Nếu bên phải không có dữ liệu tương ứng, máy sẽ điền NULL.
Ví dụ: Giả sử có em học sinh “Lê Văn C” mới nhập học, chưa xếp lớp (ma_lop_id để trống).
- Nếu dùng INNER JOIN: Em C sẽ không hiện ra trong danh sách.
- Nếu dùng LEFT JOIN:
SELECT h.ho_ten, l.ten_lop
FROM HocSinh h -- Đây là bảng TRÁI (Ưu tiên)
LEFT JOIN LopHoc l ON h.ma_lop_id = l.id_lop;
-> Kết quả: Em “Lê Văn C” vẫn hiện tên, nhưng cột ten_lop sẽ hiện là NULL.
5. Bài toán thực tế: Báo cáo tổng hợp
Hãy viết câu lệnh để tạo một bảng báo cáo đầy đủ gồm: Mã HS, Họ tên, Điểm, Tên lớp.
SELECT
h.id as "Mã HS", -- Đổi tên cột hiển thị cho đẹp
h.ho_ten as "Họ Tên",
h.diem_tb as "Điểm TB",
l.ten_lop as "Lớp"
FROM HocSinh h
INNER JOIN LopHoc l ON h.ma_lop_id = l.id_lop;
Sau khi chạy lệnh này, bạn có thể nhấn nút Export trên thanh công cụ để xuất kết quả ra file Excel gửi cho Ban giám hiệu. Đây chính là quy trình làm việc thực tế!
IV. Tổng kết (Summary)
Bạn vừa nắm trong tay kỹ thuật mạnh mẽ nhất của SQL.
- INNER JOIN: Lấy dữ liệu chung, khớp nhau giữa 2 bảng.
- LEFT JOIN: Lấy toàn bộ bảng chính, chấp nhận dữ liệu bảng phụ bị thiếu (NULL).
- Alias (AS): Đặt tên tắt giúp code gọn gàng và tránh nhầm lẫn cột.
Đến đây, bạn đã biết cách lấy dữ liệu chi tiết. Nhưng nếu thầy Hiệu trưởng hỏi: “Trường ta có tổng cộng bao nhiêu học sinh?”, “Điểm trung bình của lớp 10A1 là bao nhiêu?”. Chúng ta không thể ngồi đếm tay được. Hẹn gặp lại các bạn trong Bài 10: Các hàm gộp (Aggregate Functions) và Gom nhóm (GROUP BY).
