Study Web

Advanced SQL

SELECT Statement — Truy vấn dữ liệu

Toàn bộ SELECT statement: predicates (WHERE), operators (LIKE, BETWEEN, IN, IS NULL), aliases, concatenation, aggregate functions (COUNT, MAX, MIN, SUM, AVG), GROUP BY, và các loại JOIN (INNER, OUTER, NATURAL, multi-table).

SELECT Statement — Truy vấn dữ liệu

SELECT là lệnh đọc dữ liệu từ database. Cú pháp đầy đủ:

SELECT column1, column2, ...
FROM table_name
WHERE condition
GROUP BY column
HAVING group_condition
ORDER BY column ASC|DESC;

1. Restriction — Mệnh đề WHERE (Predicate)

Restriction là lọc dòng — chỉ lấy các dòng thỏa điều kiện. WHERE clause dùng predicate (vị từ) để xác định điều kiện.

SELECT * FROM Staff
WHERE BranchID = 101;

Các toán tử predicate quan trọng

LIKE — So khớp mẫu chuỗi

  • % = đại diện cho 0 hoặc nhiều ký tự bất kỳ
  • _ = đại diện cho đúng 1 ký tự bất kỳ
SELECT * FROM Staff WHERE StaffName LIKE 'A%';
SELECT * FROM Staff WHERE StaffName LIKE '%son';
SELECT * FROM Staff WHERE StaffName LIKE '_oe%';
SELECT * FROM Staff WHERE StaffName LIKE '%an%';

Giải thích: 'A%' = tên bắt đầu bằng A. '%son' = kết thúc bằng son. '_oe%' = ký tự thứ 2 và 3 là 'oe'. '%an%' = chứa 'an' ở bất kỳ vị trí nào.

BETWEEN — Khoảng giá trị (inclusive)

SELECT * FROM Inspection
WHERE InspDate BETWEEN '2025-01-01' AND '2025-03-31';

SELECT * FROM Product
WHERE Price BETWEEN 100 AND 500;

BETWEEN bao gồm cả hai đầu mút (inclusive on both ends).

IN và NOT IN — Kiểm tra danh sách

SELECT * FROM Staff
WHERE BranchID IN (101, 102, 105);

SELECT * FROM Staff
WHERE BranchID NOT IN (103, 104);

IS NULL và IS NOT NULL

SELECT * FROM Staff WHERE PhoneNumber IS NULL;
SELECT * FROM Staff WHERE Email IS NOT NULL;

KHÔNG ĐƯỢC viết WHERE PhoneNumber = NULL — luôn dùng IS NULL vì NULL không thể so sánh bằng toán tử =.

AND và OR — Kết hợp điều kiện

SELECT * FROM Staff
WHERE BranchID = 101 AND Gender = 'F';

SELECT * FROM Staff
WHERE BranchID = 101 OR BranchID = 102;

SELECT * FROM Staff
WHERE Gender = 'F'
  AND (BranchID = 101 OR BranchID = 102);

AND có độ ưu tiên cao hơn OR — dùng ngoặc đơn để rõ ràng.

2. Projection — Chọn cột cụ thể

Projection là chọn chỉ các cột cần thiết (thay vì SELECT *):

SELECT StaffID, StaffName, BranchID
FROM Staff;

Aliases — Đặt tên bí danh

SELECT StaffName AS 'Employee Name',
       BranchID  AS 'Branch'
FROM Staff;

SELECT s.StaffID, s.StaffName, b.BranchName
FROM Staff s
JOIN Branch b ON s.BranchID = b.BranchID;

Alias cho bảng (s, b) giúp query ngắn gọn hơn khi JOIN nhiều bảng.

Concatenation — Nối chuỗi

SELECT CONCAT(FirstName, ' ', LastName) AS 'Full Name'
FROM Person;

SELECT FirstName || ' ' || LastName AS 'Full Name'
FROM Person;

MySQL dùng CONCAT(). Chuẩn SQL dùng || (Oracle, PostgreSQL).

3. Aggregate Functions — Hàm tổng hợp

HàmMô tảVí dụ
COUNT(*)Đếm tất cả dòng (kể cả NULL)SELECT COUNT(*) FROM Staff;
COUNT(col)Đếm dòng có giá trị non-NULL trong cộtSELECT COUNT(Email) FROM Staff;
MAX(col)Giá trị lớn nhấtSELECT MAX(Score) FROM Result;
MIN(col)Giá trị nhỏ nhấtSELECT MIN(Price) FROM Product;
SUM(col)Tổng cộngSELECT SUM(Amount) FROM Order;
AVG(col)Trung bình cộngSELECT AVG(Mark) FROM Enrolment;

GROUP BY — Nhóm theo cột

GROUP BY kết hợp với aggregate functions để tính toán theo từng nhóm:

SELECT BranchID, COUNT(*) AS StaffCount
FROM Staff
GROUP BY BranchID;

Kết quả: Số nhân viên theo từng chi nhánh.

SELECT BranchID, Gender, COUNT(*) AS Count
FROM Staff
GROUP BY BranchID, Gender;
SELECT SubjectID,
       AVG(Mark)    AS AvgMark,
       MAX(Mark)    AS TopMark,
       MIN(Mark)    AS LowestMark,
       COUNT(*)     AS StudentCount
FROM Enrolment
WHERE Mark IS NOT NULL
GROUP BY SubjectID
HAVING AVG(Mark) > 60
ORDER BY AvgMark DESC;

HAVING lọc kết quả sau GROUP BY (giống WHERE nhưng dành cho group). Ví dụ trên chỉ hiển thị môn học có điểm trung bình trên 60.

4. JOINs — Kết hợp bảng

Lịch sử: Kiểu JOIN cũ (pre-SQL-92)

SELECT s.StudentName, e.SubjectID, e.Mark
FROM Student s, Enrolment e
WHERE s.StudentID = e.StudentID;

Đây là cú pháp cũ — vẫn hoạt động nhưng không nên dùng vì dễ nhầm lẫn khi nhiều bảng.

INNER JOIN — Chỉ lấy dòng khớp ở cả hai bảng

SELECT s.StudentName, e.SubjectID, e.Mark
FROM Student s
INNER JOIN Enrolment e ON s.StudentID = e.StudentID;

Ví dụ từ bài giảng: Anna không có enrolment nào → INNER JOIN sẽ loại bỏ Anna khỏi kết quả.

StudentNameSubjectIDMark
JohnCOS10175
JohnCOS10282
MaryCOS10188
Anna(không xuất hiện — không có enrolment)

LEFT OUTER JOIN / RIGHT OUTER JOIN — Giữ tất cả dòng từ một bảng

SELECT s.StudentName, e.SubjectID, e.Mark
FROM Enrolment e
RIGHT JOIN Student s ON e.StudentID = s.StudentID;

RIGHT JOIN giữ tất cả dòng từ bảng bên phải (Student). Anna xuất hiện với SubjectID = NULL và Mark = NULL:

StudentNameSubjectIDMark
JohnCOS10175
JohnCOS10282
MaryCOS10188
AnnaNULLNULL

NATURAL JOIN — Tự động khớp theo cột cùng tên

SELECT StudentName, SubjectID, Mark
FROM Student
NATURAL JOIN Enrolment;

NATURAL JOIN tự động khớp theo các cột có cùng tên ở cả hai bảng. Ít dùng vì dễ gây lỗi nếu bảng thêm cột mới.

Multi-table JOIN — JOIN nhiều bảng

SELECT s.StaffName, p.PropertyAddress, i.InspDate, i.Comments
FROM Staff s
INNER JOIN PropertyInspection i ON s.StaffID = i.StaffID
INNER JOIN Property p ON i.PropertyID = p.PropertyID
WHERE i.InspDate BETWEEN '2025-01-01' AND '2025-06-30'
ORDER BY i.InspDate;

JOIN ba bảng: Staff → PropertyInspection → Property. Mỗi JOIN thêm một bảng vào kết quả bằng cách khớp FK.

Chiến lược thực thi Query (Query Execution Strategy)

RDBMS không thực thi query theo thứ tự bạn viết. Nó tối ưu hóa:

  1. Phân tích mệnh đề WHERE để lọc sớm nhất có thể
  2. Chọn bảng có ít dòng nhất để bắt đầu
  3. Dùng index nếu có
  4. JOIN theo thứ tự tối ưu

Dùng EXPLAIN SELECT ... để xem kế hoạch thực thi của MySQL.