PHP và MySQL Bài 22: Thao tác JOIN bảng trong MySQL - Nghệ thuật lắp ghép dữ liệu (INNER JOIN, LEFT JOIN, RIGHT JOIN)
Trong thực tế vận hành, sếp của bạn sẽ không bao giờ yêu cầu: "Hãy in cho tôi danh sách các ID của trạm chứa máy bán vé". Họ sẽ yêu cầu: "Hãy xuất báo cáo gồm Tên máy bán vé và Tên trạm tương ứng".
Ngặt nghèo thay, Tên máy nằm ở bảng tvm_machines, còn Tên trạm lại nằm ở bảng stations. Để giải quyết bài toán này, SQL cung cấp mệnh đề JOIN, cho phép kết hợp các cột từ hai hay nhiều bảng dựa trên một cột chung (Khóa chính và Khóa ngoại).
Có 3 loại JOIN cơ bản nhất mà bạn sẽ sử dụng hàng ngày.
Chuẩn bị dữ liệu mẫu
Để dễ hình dung, giả sử chúng ta có 2 bảng dữ liệu đang vận hành như sau:
Bảng stations (Các trạm)
| id (PK) | station_name |
|---|---|
| 1 | Trạm Bến Thành |
| 2 | Trạm Nhà hát Thành phố |
| 3 | Trạm Ba Son (Đang xây dựng, chưa có máy) |
Bảng tvm_machines (Máy bán vé tự động)
| id | machine_code | station_id (FK) |
|---|---|---|
| 101 | TVM_BT_01 | 1 |
| 102 | TVM_NH_01 | 2 |
| 103 | TVM_TEST_09 | NULL (Máy đang nằm trong kho test, chưa gắn vào trạm nào) |
1. Mệnh đề INNER JOIN - Chỉ lấy phần giao nhau
Đây là kiểu JOIN phổ biến nhất. INNER JOIN sẽ so sánh hai bảng và CHỈ lấy ra những dòng có dữ liệu khớp nhau ở cả hai bên. Nếu một máy không thuộc trạm nào, hoặc một trạm chưa có máy nào, chúng sẽ bị loại khỏi kết quả.
Cú pháp:
SELECT cot_can_lay
FROM bang_A
INNER JOIN bang_B ON bang_A.khoa = bang_B.khoa;
Ví dụ thực tế: Lấy danh sách các máy bán vé ĐANG HOẠT ĐỘNG tại các trạm.
SELECT
tvm_machines.machine_code,
stations.station_name
FROM tvm_machines
INNER JOIN stations ON tvm_machines.station_id = stations.id;
Kết quả:
| machine_code | station_name |
|---|---|
| TVM_BT_01 | Trạm Bến Thành |
| TVM_NH_01 | Trạm Nhà hát Thành phố |
(Lưu ý: Máy TVM_TEST_09 và Trạm Ba Son hoàn toàn biến mất khỏi báo cáo vì chúng không có điểm chung với bên còn lại).
2. Mệnh đề LEFT JOIN (hoặc LEFT OUTER JOIN) - Ưu tiên bảng bên trái
Đôi khi, yêu cầu nghiệp vụ là: "Hãy liệt kê TOÀN BỘ các Trạm. Nếu trạm nào có máy bán vé thì in tên máy ra, nếu chưa có máy thì cứ để trống". Lúc này INNER JOIN vô dụng, ta phải dùng LEFT JOIN.
LEFT JOIN sẽ lấy tất cả các dòng từ bảng nằm bên trái chữ JOIN, bất chấp việc nó có tìm thấy dữ liệu khớp ở bảng bên phải hay không. Nếu không khớp, các cột của bảng bên phải sẽ mang giá trị NULL.
Ví dụ:
SELECT
stations.station_name,
tvm_machines.machine_code
FROM stations -- Bảng bên TRÁI
LEFT JOIN tvm_machines -- Bảng bên PHẢI
ON stations.id = tvm_machines.station_id;
Kết quả:
| station_name | machine_code |
|---|---|
| Trạm Bến Thành | TVM_BT_01 |
| Trạm Nhà hát Thành phố | TVM_NH_01 |
| Trạm Ba Son | NULL |
(Trạm Ba Son vẫn được giữ lại trong danh sách, cột machine_code tự động điền NULL).
3. Mệnh đề RIGHT JOIN - Ưu tiên bảng bên phải
Hoạt động giống hệt LEFT JOIN, nhưng ngược lại: Nó ưu tiên lấy toàn bộ dữ liệu của bảng nằm bên phải chữ JOIN, và điền NULL vào các cột của bảng bên trái nếu không tìm thấy liên kết.
Ví dụ: Yêu cầu kiểm kê toàn bộ máy bán vé, kể cả máy đang trong kho (chưa có trạm).
SELECT
tvm_machines.machine_code,
stations.station_name
FROM stations -- Bảng bên TRÁI
RIGHT JOIN tvm_machines -- Bảng bên PHẢI
ON stations.id = tvm_machines.station_id;
Kết quả:
| machine_code | station_name |
|---|---|
| TVM_BT_01 | Trạm Bến Thành |
| TVM_NH_01 | Trạm Nhà hát Thành phố |
| TVM_TEST_09 | NULL |
(Máy TVM_TEST_09 vẫn xuất hiện trên báo cáo, cột trạm được điền NULL).
4. Bí kíp viết truy vấn JOIN chuyên nghiệp với "Alias" (Bí danh)
Khi làm việc với các hệ thống lớn, việc viết đi viết lại tên bảng như tvm_machines.station_id = stations.id sẽ khiến đoạn SQL trở nên rất dài và rối mắt.
Các Backend Developer thường sử dụng Bí danh (Alias) bằng từ khóa AS (hoặc chỉ cần khoảng trắng) để rút gọn tên bảng ngay trong câu truy vấn.
Đoạn code tối ưu và chuyên nghiệp:
-- Dùng 't' thay cho tvm_machines, 's' thay cho stations
SELECT
t.machine_code AS 'Mã Thiết Bị',
s.station_name AS 'Tên Trạm'
FROM tvm_machines t
INNER JOIN stations s ON t.station_id = s.id
WHERE t.status = 1; -- Có thể kết hợp thoải mái với WHERE, ORDER BY
Tổng kết Bài 22 và Khép lại Phần 5
- INNER JOIN: Lấy phần giao nhau (Khớp mới lấy).
- LEFT JOIN: Lấy tất cả bên trái (Bên phải không có thì để
NULL). - RIGHT JOIN: Lấy tất cả bên phải (Bên trái không có thì để
NULL).
Đến đây, chiếc kho chứa dữ liệu MySQL đã hoàn toàn sẵn sàng, và bạn cũng đã thành thạo mọi câu thần chú (SQL) để gọi dữ liệu ra.
Nhưng, làm thế nào để mã nguồn PHP có thể "nói chuyện" được với MySQL và gửi những câu thần chú này đi một cách tự động? Chúng ta sẽ bước sang Phần 6: Kết Nối PHP & MySQL (PDO). Bài tiếp theo sẽ là Bài 23: Giới thiệu về PDO (PHP Data Objects) - Tại sao nên dùng PDO thay vì mysqli cũ?.
All rights reserved