Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Dữ liệu trong cơ sở dữ liệu quan hệ được chia thành nhiều bảng để tránh lặp lại. JOIN là cách ghép chúng lại khi truy vấn. Nắm chắc các loại JOIN là điều bắt buộc với bất kỳ ai làm backend, và cũng là câu hỏi phỏng vấn rất quen thuộc.
Giả sử có hai bảng: customers (khách hàng) và orders (đơn hàng, có cột customer_id).
customers: (1, 'An'), (2, 'Bình'), (3, 'Chi')orders: (101, customer_id=1, total=200), (102, customer_id=1, total=150), (103, customer_id=2, total=90), (104, customer_id=NULL, total=50)Chi chưa có đơn nào, và đơn 104 không gắn khách hàng nào. Hai trường hợp này sẽ giúp bạn thấy rõ khác biệt giữa các loại JOIN.
SELECT c.name, o.id, o.totalFROM customers cINNER JOIN orders o ON o.customer_id = c.id;Kết quả chỉ gồm các cặp có khớp ở cả hai bên: An (2 đơn) và Bình (1 đơn). Chi và đơn 104 đều không xuất hiện. Đây là loại JOIN thông dụng nhất và là mặc định khi bạn chỉ viết JOIN.
SELECT c.name, o.id, o.totalFROM customers cLEFT JOIN orders o ON o.customer_id = c.id;Mọi khách hàng đều xuất hiện. Với Chi, các cột của orders sẽ là NULL vì không có đơn tương ứng. LEFT JOIN rất hữu ích để tìm "những thứ chưa có":
-- Khách hàng chưa từng muaSELECT c.nameFROM customers cLEFT JOIN orders o ON o.customer_id = c.idWHERE o.id IS NULL;RIGHT JOIN là bản đối xứng, giữ nguyên bảng bên phải; trong thực tế người ta thường hoán đổi vị trí bảng và dùng LEFT JOIN cho dễ đọc. FULL OUTER JOIN giữ cả hai phía, có trong PostgreSQL nhưng MySQL không hỗ trợ trực tiếp; bạn có thể mô phỏng bằng UNION của hai phép LEFT JOIN và RIGHT JOIN.
SELECT c.name, COUNT(o.id) AS so_don, COALESCE(SUM(o.total), 0) AS tong_tienFROM customers cLEFT JOIN orders o ON o.customer_id = c.idGROUP BY c.id, c.name;Chú ý dùng COUNT(o.id) thay vì COUNT(*): với Chi, COUNT(*) trả 1 (vì dòng vẫn tồn tại), còn COUNT(o.id) trả 0 như mong muốn. COALESCE thay NULL bằng 0 cho tổng tiền.
WHERE o.total > 100 sẽ loại các dòng NULL và biến LEFT JOIN thành INNER JOIN. Nếu muốn giữ khách hàng, đặt điều kiện vào ON: LEFT JOIN orders o ON o.customer_id = c.id AND o.total > 100.id; hãy dùng bí danh bảng (alias) để chỉ rõ.Hãy hình dung bảng bên trái là danh sách chính bạn muốn giữ, rồi tự hỏi: nếu dòng bên trái không có bạn bè bên phải thì có muốn giữ không? Nếu có, dùng LEFT JOIN. Nếu không, dùng INNER JOIN. Cách tự hỏi này giải quyết được hầu hết tình huống mà không cần nhớ sơ đồ hình tròn giao nhau.
Có khi bạn cân nhắc giữa JOIN, truy vấn con (subquery) và việc chạy hai truy vấn riêng rồi ghép trong code. Không có đáp án tuyệt đối. JOIN thường rõ ràng và được tối ưu tốt khi có chỉ mục. Truy vấn con tiện cho các điều kiện kiểu EXISTS, ví dụ kiểm tra khách có ít nhất một đơn. Còn tách thành hai truy vấn đôi khi lại đơn giản và hiệu quả hơn khi dữ liệu quan hệ một-nhiều lớn, vì tránh được việc lặp lại cột của bảng cha cho mỗi dòng con. Hãy chọn cách dễ đọc nhất trước, rồi đo hiệu năng bằng EXPLAIN khi dữ liệu đủ lớn để quan trọng.
Hãy thử viết các truy vấn sau trên dữ liệu mẫu: liệt kê mỗi khách hàng kèm tổng tiền đã mua; tìm khách hàng có nhiều hơn một đơn; tìm đơn hàng không gắn khách hàng; tìm sản phẩm chưa từng được bán. Hãy luôn kiểm tra lại bằng thực hành thay vì chỉ đọc lý thuyết: dựng một dự án nhỏ, cố tình tạo lỗi, quan sát log và công cụ gỡ lỗi, rồi sửa.
JOIN không khó khi bạn thử trên dữ liệu nhỏ như ví dụ. Hãy tự tạo hai bảng, chèn vài dòng gây khó chịu (thiếu khóa, giá trị NULL) và chạy từng loại JOIN để xem kết quả. Vài phút thực hành đáng giá hơn nhiều lần đọc lý thuyết.
Muốn học bài bản và có lộ trình rõ ràng? Bạn có thể xem các khóa học trên codebem.com.
Nguyễn Đình Cường
0 comment