MySQL
Cảm ơn bạn đã đăng ký khóa học!
Tài liệu về MySQL vẫn đang được cập nhật.
Để tôn trọng công sức của tác giả, vui lòng không chia sẻ tài liệu.
Chúc bạn học tập hiệu quả!
Cài Đặt MySQL và Công Cụ Quản Lý
1. Cài Đặt MySQL
Bạn có thể tải MySQL từ trang chính thức:
MySQL có nhiều phiên bản phù hợp với từng nhu cầu:
| Phiên bản | Đặc điểm |
|---|---|
| MySQL Enterprise Edition | Dành cho doanh nghiệp lớn, hỗ trợ đầy đủ tính năng, có hỗ trợ kỹ thuật. |
| MySQL Community Edition | Miễn phí, mã nguồn mở, phù hợp cho học tập, phát triển và ứng dụng nhỏ. |
| MySQL Cluster | Dành cho hệ thống phân tán, đảm bảo tính sẵn sàng cao. |
2. Cài Đặt MySQL Workbench
MySQL Workbench là công cụ quản lý và phát triển cơ sở dữ liệu MySQL hiệu quả. Tải tại:
3. Kết Nối và Làm Việc Với MySQL
- Mở MySQL Workbench.
- Tạo kết nối mới:
- Hostname:
localhost(nếu chạy trên máy cục bộ). - Port: Mặc định là
3306. - Username:
root(hoặc tài khoản bạn đã tạo). - Password: Mật khẩu bạn đã thiết lập khi cài đặt.
- Hostname:
- Nhấn OK để kết nối.
Quản lý cơ sở dữ liệu (Database)
Tạo cơ sở dữ liệu (CREATE DATABASE)
CREATE DATABASE StudentManagementSystemTest;
Xóa cơ sở dữ liệu (DROP DATABASE)
DROP DATABASE StudentManagementSystemTest;
Sao lưu cơ sở dữ liệu
MySQL không có lệnh BACKUP tích hợp như SQL Server. Sử dụng công cụ mysqldump từ dòng lệnh:
mysqldump -u root -p StudentManagementSystem > StudentManagementSystem.sql
Khôi phục cơ sở dữ liệu
Sử dụng lệnh mysql để khôi phục từ file sao lưu:
mysql -u root -p StudentManagementSystem < StudentManagementSystem.sql
Chọn cơ sở dữ liệu để làm việc (USE)
USE StudentManagementSystem;
Liệt kê cơ sở dữ liệu
Liệt kê tất cả các cơ sở dữ liệu hiện có trong MySQL:
SHOW DATABASES;
Quản lý bảng (Table)
Tạo bảng (CREATE TABLE)
CREATE TABLE Teachers (
TeacherID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(50) NOT NULL
);
Lưu ý: MySQL sử dụng AUTO_INCREMENT thay vì IDENTITY như SQL Server.
Xóa bảng (DROP TABLE)
DROP TABLE Teachers;
Thay đổi cấu trúc bảng (ALTER TABLE)
Thêm cột mới
ALTER TABLE Students ADD Email VARCHAR(100);
Thay đổi kiểu dữ liệu của cột
ALTER TABLE Students MODIFY COLUMN Email VARCHAR(150);
Lưu ý: MySQL dùng MODIFY COLUMN thay vì ALTER COLUMN.
Đổi tên cột
ALTER TABLE Students CHANGE Email StudentEmail VARCHAR(100);
Lưu ý: MySQL dùng CHANGE thay vì sp_rename.
Xóa cột
ALTER TABLE Students DROP COLUMN StudentEmail;
Đổi tên bảng
ALTER TABLE Students RENAME TO Pupils;
Liệt kê bảng
Liệt kê danh sách các bảng trong cơ sở dữ liệu hiện tại:
SHOW TABLES;
Kiểu dữ liệu (Data Types)
Kiểu số (Numeric Data Types)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
INT | Số nguyên 4 byte, phạm vi từ -2,147,483,648 đến 2,147,483,647. | 10, 100, -50 |
BIGINT | Số nguyên 8 byte, phạm vi từ -9,223,372,036,854,775,808 đến 9,223,372,036,854,775,807. | 9223372036854775807 |
SMALLINT | Số nguyên 2 byte, phạm vi từ -32,768 đến 32,767. | 32767 |
TINYINT | Số nguyên 1 byte, phạm vi từ 0 đến 255 (hoặc -128 đến 127 nếu có dấu). | 255 |
DECIMAL(p,s) | Số thập phân có độ chính xác cao. p là tổng số chữ số, s là số chữ số sau dấu thập phân. | DECIMAL(10,2) → 12345.67 |
FLOAT | Số thực (floating-point), không cố định số chữ số sau dấu thập phân. | 3.14159 |
DOUBLE | Số thực độ chính xác cao hơn FLOAT. | 3.14159265359 |
Kiểu chuỗi (String Data Types)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
CHAR(n) | Chuỗi ký tự cố định độ dài n. | 'ABCD ' (5 ký tự, gồm cả khoảng trắng) |
VARCHAR(n) | Chuỗi ký tự có độ dài thay đổi, tối đa n ký tự. | 'John Doe' |
TEXT | Chuỗi văn bản dài, tối đa 65,535 ký tự. | 'Lorem ipsum...' |
MEDIUMTEXT | Chuỗi văn bản dài, tối đa 16,777,215 ký tự. | 'Larger text...' |
LONGTEXT | Chuỗi văn bản rất dài, tối đa 4,294,967,295 ký tự. | 'Very large text...' |
Lưu ý: MySQL không có NVARCHAR như SQL Server. Để hỗ trợ Unicode, thêm CHARACTER SET utf8mb4 khi tạo bảng:
CREATE TABLE Teachers (
TeacherID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(50) CHARACTER SET utf8mb4 NOT NULL
);
Kiểu ngày và thời gian (Date & Time Data Types)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
DATE | Lưu ngày tháng, phạm vi từ 1000-01-01 đến 9999-12-31. | '2025-03-01' |
TIME | Lưu thời gian, phạm vi từ -838:59:59 đến 838:59:59. | '12:30:45' |
DATETIME | Lưu ngày và giờ, phạm vi từ 1000-01-01 00:00:00 đến 9999-12-31 23:59:59. | '2025-03-01 12:30:45' |
TIMESTAMP | Lưu ngày giờ, tự động cập nhật khi thay đổi bản ghi, phạm vi từ 1970-01-01 00:00:00 UTC đến 2038-01-19 03:14:07 UTC. | '2025-03-01 12:30:45' |
Kiểu dữ liệu nhị phân và định danh (Binary & Unique Identifier)
| Kiểu dữ liệu | Mô tả | Ví dụ |
|---|---|---|
BINARY(n) | Chuỗi nhị phân cố định độ dài n. | 0x1A2B3C |
VARBINARY(n) | Chuỗi nhị phân có độ dài thay đổi, tối đa n byte. | 0x1A2B3C |
BLOB | Dữ liệu nhị phân lớn, tối đa 65,535 byte. | Hình ảnh, file |
Lưu ý: MySQL không có kiểu UNIQUEIDENTIFIER như SQL Server. Để tạo ID duy nhất, sử dụng hàm UUID():
SELECT UUID(); -- Trả về: '550e8400-e29b-41d4-a716-446655440000'
Ví dụ tạo bảng với kiểu dữ liệu phổ biến
CREATE TABLE Employees (
EmployeeID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(100) CHARACTER SET utf8mb4 NOT NULL,
Birthdate DATE,
WorkStartTime TIME,
Salary DECIMAL(10,2),
ProfilePicture BLOB,
IsActive TINYINT(1) DEFAULT 1
);
Ví dụ tạo bảng với tất cả kiểu dữ liệu
CREATE TABLE AllDataTypesExample (
ID CHAR(36) DEFAULT (UUID()) PRIMARY KEY, -- ID duy nhất, sử dụng UUID thay cho UNIQUEIDENTIFIER
IntNumber INT NOT NULL, -- Số nguyên 4 byte
BigIntNumber BIGINT, -- Số nguyên lớn 8 byte
SmallIntNumber SMALLINT, -- Số nguyên nhỏ 2 byte
TinyIntNumber TINYINT, -- Số nguyên rất nhỏ 1 byte
DecimalNumber DECIMAL(10,2), -- Số thập phân
FloatNumber FLOAT, -- Số thực
DoubleNumber DOUBLE, -- Số thực độ chính xác cao
IsActive TINYINT(1) DEFAULT 1, -- Boolean (0: false, 1: true)
FixedText CHAR(10), -- Chuỗi cố định 10 ký tự
VariableText VARCHAR(255), -- Chuỗi thay đổi tối đa 255 ký tự
LongText LONGTEXT, -- Văn bản rất dài
BirthDate DATE, -- Chỉ chứa ngày
WorkStartTime TIME, -- Chỉ chứa giờ
FullDateTime DATETIME, -- Ngày và giờ
ProfilePicture BLOB, -- Lưu trữ dữ liệu nhị phân
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP -- Ngày tạo, mặc định thời điểm hiện tại
);
Lưu ý:
- MySQL không có
MONEYnhư SQL Server, thay vào đó dùngDECIMAL. TEXTtrong MySQL không bị lỗi thời như SQL Server, nhưng có các biến thể nhưMEDIUMTEXT,LONGTEXT.DATETIME2không tồn tại trong MySQL, chỉ cóDATETIMEhoặcTIMESTAMP.
Biến (Variable)
Trong MySQL, biến được sử dụng trong các script hoặc stored procedure/function. Có hai loại biến chính: biến người dùng (user-defined) và biến hệ thống (system variables).
Khai báo biến (DECLARE)
Biến trong MySQL được khai báo trong stored procedure hoặc function bằng DECLARE:
DELIMITER //
CREATE PROCEDURE Example()
BEGIN
DECLARE StudentName VARCHAR(50);
DECLARE BirthYear INT;
END //
DELIMITER ;
Lưu ý: MySQL yêu cầu thay đổi DELIMITER để viết các khối lệnh như stored procedure.
Gán giá trị cho biến (SET, SELECT)
Gán giá trị bằng SET
SET @StudentName = 'Hoàng Hiệp';
SELECT @StudentName;
Gán giá trị bằng SELECT
SELECT @BirthYear := YEAR(Birthdate)
FROM Students WHERE StudentID = 1;
Lưu ý: MySQL dùng := trong SELECT để gán giá trị, hoặc dùng SET.
Sử dụng biến
SET @Year = 2000;
SELECT * FROM Students WHERE YEAR(Birthdate) = @Year;
Biến bảng (Table Variable)
MySQL không hỗ trợ biến bảng như SQL Server. Thay vào đó, sử dụng bảng tạm (temporary table):
CREATE TEMPORARY TABLE StudentTable (
StudentID INT PRIMARY KEY,
Name VARCHAR(50),
Birthdate DATE
);
INSERT INTO StudentTable (StudentID, Name, Birthdate)
VALUES (1, 'John Doe', '2000-05-15');
SELECT * FROM StudentTable;
DROP TEMPORARY TABLE StudentTable;
Biến hệ thống
MySQL có các biến hệ thống bắt đầu bằng @@:
@@version- Phiên bản MySQL.@@row_count- Số dòng bị ảnh hưởng bởi truy vấn trước đó.
SELECT @@version;
SELECT ROW_COUNT();
Lưu ý: MySQL dùng ROW_COUNT() thay vì @@ROWCOUNT.
Hàm (Functions)
Hàm xử lý chuỗi (String Functions)
| Hàm | Chức năng | Ví dụ |
|---|---|---|
LENGTH(string) | Trả về độ dài chuỗi (byte) | LENGTH('Hello') → 5 |
CHAR_LENGTH(string) | Trả về số ký tự | CHAR_LENGTH('Xin chào') → 8 |
LEFT(string, n) | Lấy n ký tự bên trái | LEFT('MySQL', 2) → 'My' |
RIGHT(string, n) | Lấy n ký tự bên phải | RIGHT('MySQL', 3) → 'SQL' |
SUBSTRING(string, start, length) | Cắt chuỗi con | SUBSTRING('MySQL', 3, 3) → 'SQL' |
UPPER(string) | Chuyển thành chữ in hoa | UPPER('mysql') → 'MYSQL' |
LOWER(string) | Chuyển thành chữ thường | LOWER('MySQL') → 'mysql' |
TRIM(string) | Xóa khoảng trắng hai bên | TRIM(' MySQL ') → 'MySQL' |
REPLACE(string, old, new) | Thay thế chuỗi con | REPLACE('Hello MySQL', 'MySQL', 'World') → 'Hello World' |
LOCATE(substring, string) | Vị trí của chuỗi con | LOCATE('SQL', 'MySQL') → 3 |
CONCAT(string1, string2, …) | Nối chuỗi | CONCAT('My', 'SQL') → 'MySQL' |
Lưu ý: MySQL không có CHARINDEX, dùng LOCATE thay thế.
Hàm xử lý số (Numeric Functions)
| Hàm | Chức năng | Ví dụ |
|---|---|---|
ABS(number) | Trị tuyệt đối | ABS(-5) → 5 |
CEIL(number) | Làm tròn lên | CEIL(4.2) → 5 |
FLOOR(number) | Làm tròn xuống | FLOOR(4.9) → 4 |
ROUND(number, decimals) | Làm tròn số | ROUND(4.567, 2) → 4.57 |
POW(base, exponent) | Lũy thừa | POW(2, 3) → 8 |
SQRT(number) | Căn bậc hai | SQRT(9) → 3 |
RAND() | Tạo số ngẫu nhiên từ 0 đến 1 | RAND() → 0.684 |
Lưu ý: MySQL dùng POW thay vì POWER.
Hàm xử lý ngày tháng (Date Functions)
| Hàm | Chức năng | Ví dụ |
|---|---|---|
NOW() | Trả về ngày giờ hiện tại | NOW() → '2025-03-15 10:15:30' |
CURDATE() | Trả về ngày hiện tại | CURDATE() → '2025-03-15' |
CURTIME() | Trả về giờ hiện tại | CURTIME() → '10:15:30' |
YEAR(date) | Trả về năm | YEAR('2025-03-15') → 2025 |
MONTH(date) | Trả về tháng | MONTH('2025-03-15') → 3 |
DAY(date) | Trả về ngày | DAY('2025-03-15') → 15 |
DATE_ADD(date, INTERVAL value unit) | Cộng thêm giá trị | DATE_ADD('2025-03-15', INTERVAL 10 DAY) → '2025-03-25' |
DATEDIFF(date1, date2) | Khoảng cách giữa hai ngày (ngày) | DATEDIFF('2025-03-10', '2025-03-01') → 9 |
DATE_FORMAT(date, format) | Định dạng ngày | DATE_FORMAT(NOW(), '%d/%m/%Y') → '15/03/2025' |
Lưu ý:
- MySQL không có
EOMONTH, nhưng có thể dùngLAST_DAY:SELECT LAST_DAY('2025-03-15'); -- Trả về '2025-03-31'
Hàm nâng cao (Advanced Functions)
Hàm chuyển đổi dữ liệu
| Hàm | Chức năng | Ví dụ |
|---|---|---|
CAST(expression AS type) | Chuyển kiểu dữ liệu | CAST(123 AS CHAR) → '123' |
CONVERT(expression, type) | Chuyển kiểu dữ liệu | CONVERT(123, CHAR) → '123' |
Hàm xử lý giá trị NULL
| Hàm | Chức năng | Ví dụ |
|---|---|---|
COALESCE(val1, val2, …) | Trả về giá trị không NULL đầu tiên | COALESCE(NULL, 'MySQL', 'World') → 'MySQL' |
IFNULL(expression, replacement) | Thay thế giá trị NULL | IFNULL(NULL, 'N/A') → 'N/A' |
NULLIF(val1, val2) | Trả về NULL nếu hai giá trị bằng nhau | NULLIF(10, 10) → NULL |
Ràng buộc dữ liệu
Ràng buộc giúp duy trì tính toàn vẹn dữ liệu bằng cách kiểm soát các giá trị được nhập vào bảng.
Tổng quan các ràng buộc
| Ràng buộc | Phân loại | Chức năng |
|---|---|---|
NOT NULL | Thuộc tính cột | Không cho phép giá trị NULL trong cột. |
UNIQUE | Ràng buộc | Đảm bảo tất cả giá trị trong cột là duy nhất. |
PRIMARY KEY | Ràng buộc | Kết hợp NOT NULL và UNIQUE, mỗi bảng chỉ có một khóa chính. |
FOREIGN KEY | Ràng buộc | Tạo mối quan hệ giữa hai bảng, đảm bảo dữ liệu hợp lệ. |
CHECK | Ràng buộc | Kiểm tra giá trị nhập vào thỏa mãn điều kiện cụ thể. |
DEFAULT | Thuộc tính cột | Gán giá trị mặc định cho cột nếu không có giá trị nhập. |
AUTO_INCREMENT | Thuộc tính cột | Tạo cột tự động tăng giá trị cho mỗi bản ghi. |
NOT NULL
Bắt buộc cột phải có giá trị, không được để trống.
CREATE TABLE Teachers (
TeacherID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(50) NOT NULL
);
UNIQUE
Đảm bảo giá trị trong cột không bị trùng lặp.
CREATE TABLE Doctors (
DoctorID INT AUTO_INCREMENT PRIMARY KEY,
Email VARCHAR(100) UNIQUE
);
PRIMARY KEY
Định nghĩa khóa chính để xác định duy nhất mỗi bản ghi.
CREATE TABLE Departments (
DeptID INT PRIMARY KEY,
DeptName VARCHAR(50) NOT NULL
);
Hoặc trên nhiều cột:
CREATE TABLE CourseRegistrations (
StudentID INT,
CourseID INT,
PRIMARY KEY (StudentID, CourseID)
);
FOREIGN KEY
Tạo khóa ngoại, đảm bảo dữ liệu giữa các bảng có liên kết hợp lệ.
CREATE TABLE Enrollments (
StudentID INT,
CourseID INT,
FOREIGN KEY (StudentID) REFERENCES Students(StudentID)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
ON DELETE SET NULL
ON UPDATE NO ACTION
);
ON DELETE CASCADE: Xóa bản ghi trong bảng cha sẽ xóa các bản ghi liên quan trong bảng con.ON UPDATE CASCADE: Cập nhật khóa chính trong bảng cha sẽ cập nhật khóa ngoại trong bảng con.ON DELETE SET NULL: Xóa bản ghi trong bảng cha sẽ đặt giá trị khóa ngoại trong bảng con thànhNULL.ON UPDATE NO ACTION: Từ chối cập nhật nếu vi phạm ràng buộc khóa ngoại.
CHECK
Đảm bảo dữ liệu nhập vào thỏa mãn điều kiện.
CREATE TABLE Employees (
EmployeeID INT AUTO_INCREMENT PRIMARY KEY,
Age INT CHECK (Age >= 18)
);
Lưu ý: Một số engine như MyISAM không hỗ trợ CHECK. Dùng engine InnoDB để hỗ trợ đầy đủ.
DEFAULT
Đặt giá trị mặc định cho cột khi không có giá trị nhập.
CREATE TABLE Orders (
OrderID INT PRIMARY KEY,
OrderDate DATETIME DEFAULT CURRENT_TIMESTAMP
);
AUTO_INCREMENT
Tạo cột có giá trị tự động tăng, thường dùng cho khóa chính.
CREATE TABLE Employees (
EmployeeID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(100) NOT NULL
);
AUTO_INCREMENTbắt đầu từ 1 và tăng 1 mặc định. Có thể thay đổi:ALTER TABLE Employees AUTO_INCREMENT = 100; -- Bắt đầu từ 100
Lấy giá trị AUTO_INCREMENT mới nhất
SELECT LAST_INSERT_ID(); -- Lấy giá trị AUTO_INCREMENT vừa chèn
Đặt lại giá trị AUTO_INCREMENT
ALTER TABLE Employees AUTO_INCREMENT = 1; -- Đặt lại về 1
Ví dụ tạo bảng có đầy đủ ràng buộc
CREATE TABLE Departments (
DeptID INT PRIMARY KEY,
DeptName VARCHAR(100) NOT NULL UNIQUE
);
CREATE TABLE Employees (
EmployeeID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
Email VARCHAR(100) UNIQUE,
Birthdate DATE CHECK (Birthdate >= '1990-01-01'),
Salary DECIMAL(10,2) DEFAULT 1000.00,
DepartmentID INT,
FOREIGN KEY (DepartmentID) REFERENCES Departments(DeptID)
);
Thao tác dữ liệu
Thêm dữ liệu (INSERT INTO)
Thêm một sinh viên mới vào bảng Students:
INSERT INTO Students (Name, Birthdate)
VALUES ('Hoàng Hiệp', '2001-05-20');
Cập nhật dữ liệu (UPDATE)
Cập nhật ngày sinh cho sinh viên có StudentID = 4:
UPDATE Students
SET Birthdate = '2000-06-15'
WHERE StudentID = 4;
Xóa dữ liệu (DELETE)
Xóa sinh viên có StudentID = 2:
DELETE FROM Students
WHERE StudentID = 2;
Lưu ý: Nếu không có WHERE, toàn bộ dữ liệu trong bảng sẽ bị xóa.
Xóa toàn bộ dữ liệu (TRUNCATE TABLE)
Xóa toàn bộ dữ liệu trong bảng và đặt lại giá trị AUTO_INCREMENT:
TRUNCATE TABLE Students;
Sao chép dữ liệu vào bảng mới (CREATE TABLE AS)
Tạo bảng mới và sao chép dữ liệu từ bảng hiện có:
CREATE TABLE StudentsBackup AS
SELECT * FROM Students;
Chèn dữ liệu từ bảng khác (INSERT INTO SELECT)
Chèn dữ liệu từ Students vào StudentsBackup:
INSERT INTO StudentsBackup (Name, Birthdate)
SELECT Name, Birthdate FROM Students;
Hợp nhất dữ liệu (INSERT ... ON DUPLICATE KEY UPDATE)
MySQL không có MERGE như SQL Server, thay vào đó dùng INSERT ... ON DUPLICATE KEY UPDATE:
INSERT INTO StudentsBackup (StudentID, Name, Birthdate)
SELECT StudentID, Name, Birthdate FROM Students
ON DUPLICATE KEY UPDATE
Name = VALUES(Name),
Birthdate = VALUES(Birthdate);
Kiểm tra tồn tại (EXISTS)
Kiểm tra xem sinh viên “Hoàng Hiệp” đã tồn tại chưa trước khi thêm mới:
IF EXISTS (SELECT 1 FROM Students WHERE Name = 'Hoàng Hiệp') THEN
SELECT 'Sinh viên đã tồn tại';
ELSE
INSERT INTO Students (Name, Birthdate) VALUES ('Hoàng Hiệp', '2001-05-20');
END IF;
Lưu ý: Cú pháp này dùng trong stored procedure hoặc script với DELIMITER.
Lấy dữ liệu bị ảnh hưởng (RETURNING)
MySQL không có OUTPUT như SQL Server. Để lấy dữ liệu bị ảnh hưởng, sử dụng truy vấn riêng:
SELECT StudentID, Name
FROM Students
WHERE Birthdate < '2000-01-01';
DELETE FROM Students
WHERE Birthdate < '2000-01-01';
Toán tử và điều kiện lọc
Toán tử so sánh
Các toán tử phổ biến trong MySQL:
| Toán tử | Chức năng |
|---|---|
= | So sánh bằng |
!= hoặc <> | So sánh khác |
> | Lớn hơn |
< | Nhỏ hơn |
>= | Lớn hơn hoặc bằng |
<= | Nhỏ hơn hoặc bằng |
BETWEEN | Trong khoảng |
IN | Trong danh sách |
LIKE | Khớp mẫu ký tự |
AND | Và |
OR | Hoặc |
NOT | Phủ định |
AND
Lấy sinh viên có tên bắt đầu bằng 'P' và sinh sau 2000:
SELECT * FROM Students
WHERE Name LIKE 'P%' AND Birthdate > '2000-01-01';
OR
Lấy sinh viên có tên là “Trần Nhật Anh” hoặc “Vũ Khánh Linh”:
SELECT * FROM Students
WHERE Name = 'Trần Nhật Anh' OR Name = 'Vũ Khánh Linh';
NOT
Lấy tất cả sinh viên trừ “Hoàng Hiệp”:
SELECT * FROM Students
WHERE Name != 'Hoàng Hiệp';
LIKE
Tìm sinh viên có tên bắt đầu bằng “T”:
SELECT * FROM Students
WHERE Name LIKE 'T%';
IN
Lấy sinh viên có tên trong danh sách:
SELECT * FROM Students
WHERE Name IN ('Vũ Khánh Linh', 'Trần Nhật Anh', 'Lê Thu Hà');
BETWEEN
Lấy sinh viên sinh từ 2000 đến 2003:
SELECT * FROM Students
WHERE Birthdate BETWEEN '2000-01-01' AND '2003-12-31';
IS NULL và IS NOT NULL
Lấy sinh viên chưa có ngày sinh:
SELECT * FROM Students WHERE Birthdate IS NULL;
IS NULL và IS NOT NULL (tiếp tục từ phần trước)
Lấy danh sách sinh viên đã có ngày sinh:
SELECT * FROM Students WHERE Birthdate IS NOT NULL;
IF ELSE
IF ELSE trong MySQL được sử dụng trong stored procedure hoặc function để điều khiển luồng thực thi.
Cú pháp
DELIMITER //
CREATE PROCEDURE CheckStudentCount()
BEGIN
DECLARE TotalStudents INT;
SELECT COUNT(*) INTO TotalStudents FROM Students;
IF TotalStudents > 100 THEN
SELECT 'Có hơn 100 sinh viên trong hệ thống.';
ELSE
SELECT 'Số lượng sinh viên ít hơn hoặc bằng 100.';
END IF;
END //
DELIMITER ;
Lưu ý: MySQL không hỗ trợ IF ELSE trực tiếp trong truy vấn SELECT như SQL Server. Thay vào đó, dùng IF() hoặc CASE.
IF (Hàm)
Hàm IF() trong MySQL dùng trong truy vấn để thay thế IIF của SQL Server.
Cú pháp
IF(điều_kiện, giá_trị_nếu_đúng, giá_trị_nếu_sai)
Kiểm tra sinh viên đủ 18 tuổi:
SELECT
StudentID,
Name,
IF(DATEDIFF(CURDATE(), Birthdate) / 365 >= 18, 'Đủ tuổi', 'Chưa đủ tuổi') AS AgeStatus
FROM Students;
CASE WHEN
CASE WHEN hỗ trợ nhiều điều kiện và có thể dùng trong SELECT, WHERE, ORDER BY, v.v.
Cú pháp 1
CASE
WHEN điều_kiện_1 THEN giá_trị_1
WHEN điều_kiện_2 THEN giá_trị_2
ELSE giá_trị_mặc_định
END
Xác định nhóm tuổi của sinh viên:
SELECT
Name,
CASE
WHEN DATEDIFF(CURDATE(), Birthdate) / 365 < 18 THEN 'Teen'
WHEN DATEDIFF(CURDATE(), Birthdate) / 365 BETWEEN 18 AND 24 THEN 'Young Adult'
ELSE 'Adult'
END AS AgeGroup
FROM Students;
Cú pháp 2
CASE biểu_thức
WHEN giá_trị_1 THEN kết_quả_1
WHEN giá_trị_2 THEN kết_quả_2
ELSE kết_quả_mặc_định
END
Xác định phân loại khóa học dựa trên CourseID:
SELECT
CourseID,
Name,
CASE CourseID
WHEN 1 THEN 'Toán học'
WHEN 2 THEN 'Lập trình'
ELSE 'Ngôn ngữ'
END AS CourseCategory
FROM Courses;
Truy vấn dữ liệu
SELECT
Truy vấn dữ liệu từ bảng:
SELECT * FROM Students;
Lấy tên và ngày sinh:
SELECT Name, Birthdate FROM Students;
SELECT DISTINCT
Loại bỏ các giá trị trùng lặp:
SELECT DISTINCT Name FROM Students;
WHERE
Lọc dữ liệu theo điều kiện:
SELECT * FROM Students WHERE Birthdate > '2001-01-01';
ORDER BY
Sắp xếp kết quả:
SELECT * FROM Students ORDER BY Name ASC;
SELECT * FROM Students ORDER BY Birthdate DESC;
LIMIT
Giới hạn số lượng kết quả (tương đương TOP trong SQL Server):
SELECT * FROM Students ORDER BY Birthdate DESC LIMIT 3; -- Lấy 3 sinh viên trẻ nhất
Kết hợp bảng
INNER JOIN
Lấy dữ liệu chỉ khi có sự liên kết giữa các bảng:
SELECT s.StudentID, s.Name AS StudentName, c.CourseID, c.Name AS CourseName
FROM Students s
INNER JOIN Enrollments e ON s.StudentID = e.StudentID
INNER JOIN Courses c ON e.CourseID = c.CourseID;
LEFT JOIN
Lấy tất cả dữ liệu từ bảng bên trái:
SELECT s.StudentID, s.Name AS StudentName, c.CourseID, c.Name AS CourseName
FROM Students s
LEFT JOIN Enrollments e ON s.StudentID = e.StudentID
LEFT JOIN Courses c ON e.CourseID = c.CourseID;
RIGHT JOIN
Lấy tất cả dữ liệu từ bảng bên phải:
SELECT s.StudentID, s.Name AS StudentName, c.CourseID, c.Name AS CourseName
FROM Students s
RIGHT JOIN Enrollments e ON s.StudentID = e.StudentID
RIGHT JOIN Courses c ON e.CourseID = c.CourseID;
FULL JOIN
MySQL không hỗ trợ FULL JOIN trực tiếp. Dùng LEFT JOIN kết hợp RIGHT JOIN với UNION:
SELECT s.StudentID, s.Name AS StudentName, c.CourseID, c.Name AS CourseName
FROM Students s
LEFT JOIN Enrollments e ON s.StudentID = e.StudentID
LEFT JOIN Courses c ON e.CourseID = c.CourseID
UNION
SELECT s.StudentID, s.Name AS StudentName, c.CourseID, c.Name AS CourseName
FROM Students s
RIGHT JOIN Enrollments e ON s.StudentID = e.StudentID
RIGHT JOIN Courses c ON e.CourseID = c.CourseID
WHERE s.StudentID IS NULL;
CROSS JOIN
Kết hợp tất cả các hàng của hai bảng:
SELECT s.Name AS StudentName, c.Name AS CourseName
FROM Students s
CROSS JOIN Courses c;
SELF JOIN
Nối bảng với chính nó:
ALTER TABLE Students
ADD MentorID INT NULL;
INSERT INTO Students (Name, MentorID) VALUES
('Nguyen Van A', 1),
('Tran Thi B', 2);
SELECT s1.StudentID, s1.Name AS StudentName,
s1.MentorID AS MentorID, s2.Name AS MentorName
FROM Students s1
LEFT JOIN Students s2 ON s1.MentorID = s2.StudentID;
UNION
Kết hợp dữ liệu từ nhiều truy vấn, loại bỏ trùng lặp:
SELECT Name FROM Students
UNION
SELECT Name FROM Courses;
UNION ALL
Kết hợp dữ liệu, không loại bỏ trùng lặp:
SELECT Name FROM Students
UNION ALL
SELECT Name FROM Courses;
EXCEPT và INTERSECT
MySQL không hỗ trợ EXCEPT hoặc INTERSECT trực tiếp. Dùng subquery hoặc LEFT JOIN để thay thế:
Thay thế EXCEPT
Lấy tên trong Students không có trong Courses:
SELECT Name FROM Students
WHERE Name NOT IN (SELECT Name FROM Courses);
Thay thế INTERSECT
Lấy tên xuất hiện trong cả hai bảng:
SELECT Name FROM Students
WHERE Name IN (SELECT Name FROM Courses);
Hàm tổng hợp
COUNT
Đếm số lượng bản ghi:
SELECT COUNT(*) AS TotalStudents FROM Students;
SUM
Tính tổng giá trị:
ALTER TABLE Enrollments ADD Fee INT;
SELECT SUM(Fee) AS TotalFees FROM Enrollments;
AVG
Tính giá trị trung bình:
SELECT AVG(Fee) AS AverageFee FROM Enrollments;
MIN và MAX
Tìm giá trị nhỏ nhất và lớn nhất:
SELECT MIN(Birthdate) AS Oldest, MAX(Birthdate) AS Youngest FROM Students;
GROUP BY
Nhóm dữ liệu:
SELECT c.CourseID, c.Name, SUM(e.Fee) AS TotalFee
FROM Courses c
LEFT JOIN Enrollments e ON c.CourseID = e.CourseID
GROUP BY c.CourseID, c.Name;
HAVING
Lọc dữ liệu sau khi tổng hợp:
SELECT c.CourseID, c.Name, SUM(e.Fee) AS TotalFee
FROM Courses c
LEFT JOIN Enrollments e ON c.CourseID = e.CourseID
GROUP BY c.CourseID, c.Name
HAVING SUM(e.Fee) >= 20;
COUNT DISTINCT
Đếm số lượng giá trị không trùng lặp:
SELECT c.CourseID, c.Name, COUNT(DISTINCT e.StudentID) AS EnrolledStudents
FROM Courses c
LEFT JOIN Enrollments e ON c.CourseID = e.CourseID
GROUP BY c.CourseID, c.Name;
STRING_AGG
MySQL không có hàm STRING_AGG như SQL Server. Thay vào đó, sử dụng GROUP_CONCAT để nối các chuỗi trong nhóm.
Cú pháp
GROUP_CONCAT(cột [SEPARATOR 'ký_tự_phân_cách'])
Ví dụ: Nối danh sách tên sinh viên theo khóa học:
SELECT
c.CourseID,
c.Name AS CourseName,
GROUP_CONCAT(s.Name SEPARATOR ', ') AS StudentNames
FROM Courses c
LEFT JOIN Enrollments e ON c.CourseID = e.CourseID
LEFT JOIN Students s ON e.StudentID = s.StudentID
GROUP BY c.CourseID, c.Name;
Lưu ý:
- Mặc định,
GROUP_CONCATphân cách bằng dấu phẩy (,). - Giới hạn độ dài mặc định là 1024 ký tự, có thể tăng bằng cách thay đổi biến hệ thống:
SET SESSION group_concat_max_len = 1000000;
Index (Chỉ mục)
Tổng quan
Chỉ mục tăng tốc độ truy vấn nhưng có thể làm chậm thao tác ghi (INSERT, UPDATE, DELETE).
Tạo chỉ mục (CREATE INDEX)
Tạo chỉ mục trên cột Name trong bảng Students:
CREATE INDEX idx_name ON Students(Name);
Tạo chỉ mục duy nhất (UNIQUE INDEX)
Đảm bảo không có giá trị trùng lặp:
CREATE UNIQUE INDEX idx_email ON Students(Email);
Tạo chỉ mục trên nhiều cột
CREATE INDEX idx_student_course ON Enrollments(StudentID, CourseID);
Xóa chỉ mục (DROP INDEX)
DROP INDEX idx_name ON Students;
Xem danh sách chỉ mục
SHOW INDEX FROM Students;
Chỉ mục tự động
- Cột
PRIMARY KEYvàUNIQUEtự động được tạo chỉ mục. - Không cần tạo chỉ mục thủ công cho các cột này.
View (Khung nhìn)
Tạo View (CREATE VIEW)
Tạo khung nhìn chứa thông tin sinh viên và khóa học:
CREATE VIEW StudentCourseView AS
SELECT
s.StudentID,
s.Name AS StudentName,
c.CourseID,
c.Name AS CourseName
FROM Students s
LEFT JOIN Enrollments e ON s.StudentID = e.StudentID
LEFT JOIN Courses c ON e.CourseID = c.CourseID;
Sử dụng View
SELECT * FROM StudentCourseView WHERE CourseName = 'Toán học';
Cập nhật View (CREATE OR REPLACE VIEW)
CREATE OR REPLACE VIEW StudentCourseView AS
SELECT
s.StudentID,
s.Name AS StudentName,
c.CourseID,
c.Name AS CourseName,
e.Fee
FROM Students s
LEFT JOIN Enrollments e ON s.StudentID = e.StudentID
LEFT JOIN Courses c ON e.CourseID = e.CourseID;
Xóa View (DROP VIEW)
DROP VIEW StudentCourseView;
Lưu ý: View trong MySQL chỉ đọc (read-only) trừ khi đáp ứng các điều kiện cụ thể (ví dụ: không dùng JOIN, GROUP BY).
Stored Procedure
Tạo Stored Procedure (CREATE PROCEDURE)
Tạo thủ tục lưu trữ để thêm sinh viên mới:
DELIMITER //
CREATE PROCEDURE AddStudent(IN studentName VARCHAR(100), IN birthDate DATE)
BEGIN
INSERT INTO Students (Name, Birthdate)
VALUES (studentName, birthDate);
END //
DELIMITER ;
Gọi Stored Procedure
CALL AddStudent('Nguyen Van A', '2001-01-15');
Stored Procedure với tham số đầu ra
Lấy số lượng sinh viên:
DELIMITER //
CREATE PROCEDURE GetStudentCount(OUT total INT)
BEGIN
SELECT COUNT(*) INTO total FROM Students;
END //
DELIMITER ;
CALL GetStudentCount(@total);
SELECT @total AS TotalStudents;
Xóa Stored Procedure (DROP PROCEDURE)
DROP PROCEDURE AddStudent;
Xem danh sách Stored Procedure
SHOW PROCEDURE STATUS WHERE Db = 'StudentManagementSystem';
Function (Hàm người dùng)
Tạo Function (CREATE FUNCTION)
Tạo hàm tính tuổi từ ngày sinh:
DELIMITER //
CREATE FUNCTION CalculateAge(birthDate DATE)
RETURNS INT DETERMINISTIC
BEGIN
RETURN DATEDIFF(CURDATE(), birthDate) / 365;
END //
DELIMITER ;
DETERMINISTIC: Hàm luôn trả về cùng kết quả với cùng đầu vào.
Sử dụng Function
SELECT Name, CalculateAge(Birthdate) AS Age FROM Students;
Xóa Function (DROP FUNCTION)
DROP FUNCTION CalculateAge;
Xem danh sách Function
SHOW FUNCTION STATUS WHERE Db = 'StudentManagementSystem';
Trigger
Tạo Trigger (CREATE TRIGGER)
Tạo trigger tự động cập nhật thời gian chỉnh sửa khi cập nhật sinh viên:
ALTER TABLE Students ADD LastModified DATETIME;
DELIMITER //
CREATE TRIGGER UpdateLastModified
BEFORE UPDATE ON Students
FOR EACH ROW
BEGIN
SET NEW.LastModified = NOW();
END //
DELIMITER ;
Kiểm tra Trigger
UPDATE Students SET Name = 'Nguyen Van B' WHERE StudentID = 1;
SELECT * FROM Students WHERE StudentID = 1;
Xóa Trigger (DROP TRIGGER)
DROP TRIGGER UpdateLastModified;
Xem danh sách Trigger
SHOW TRIGGERS FROM StudentManagementSystem;
Transaction (Giao dịch)
Bắt đầu giao dịch (START TRANSACTION)
START TRANSACTION;
INSERT INTO Students (Name, Birthdate) VALUES ('Tran Thi C', '2002-03-10');
INSERT INTO Enrollments (StudentID, CourseID, Fee) VALUES (LAST_INSERT_ID(), 1, 500);
Xác nhận giao dịch (COMMIT)
COMMIT;
Hoàn tác giao dịch (ROLLBACK)
Nếu có lỗi, hoàn tác:
START TRANSACTION;
INSERT INTO Students (Name, Birthdate) VALUES ('Le Van D', '2003-04-12');
-- Giả sử có lỗi xảy ra
ROLLBACK;
Điểm lưu trữ (SAVEPOINT)
START TRANSACTION;
INSERT INTO Students (Name, Birthdate) VALUES ('Pham Van E', '2004-05-20');
SAVEPOINT savepoint1;
INSERT INTO Enrollments (StudentID, CourseID, Fee) VALUES (LAST_INSERT_ID(), 2, 600);
-- Hoàn tác đến savepoint
ROLLBACK TO savepoint1;
COMMIT;
Cursor (Con trỏ)
Sử dụng Cursor
Duyệt qua danh sách sinh viên và in tên:
DELIMITER //
CREATE PROCEDURE PrintStudentNames()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE sName VARCHAR(100);
DECLARE studentCursor CURSOR FOR SELECT Name FROM Students;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN studentCursor;
read_loop: LOOP
FETCH studentCursor INTO sName;
IF done THEN
LEAVE read_loop;
END IF;
SELECT sName;
END LOOP;
CLOSE studentCursor;
END //
DELIMITER ;
CALL PrintStudentNames();
Quản lý người dùng
Tạo người dùng (CREATE USER)
CREATE USER 'student_admin'@'localhost' IDENTIFIED BY 'secure_password';
Phân quyền (GRANT)
Cấp quyền cho người dùng:
GRANT SELECT, INSERT, UPDATE ON StudentManagementSystem.* TO 'student_admin'@'localhost';
Thu hồi quyền (REVOKE)
REVOKE INSERT, UPDATE ON StudentManagementSystem.* FROM 'student_admin'@'localhost';
Xóa người dùng (DROP USER)
DROP USER 'student_admin'@'localhost';
Xem danh sách người dùng
SELECT User, Host FROM mysql.user;
Tối ưu hóa truy vấn (Optimization)
EXPLAIN
MySQL sử dụng EXPLAIN để phân tích cách thực thi truy vấn (tương đương Execution Plan trong SQL Server):
EXPLAIN SELECT s.Name, c.Name AS CourseName
FROM Students s
INNER JOIN Enrollments e ON s.StudentID = e.StudentID
INNER JOIN Courses c ON e.CourseID = c.CourseID
WHERE s.Birthdate > '2000-01-01';
Kết quả hiển thị:
id: ID của truy vấn.select_type: Loại truy vấn (SIMPLE, SUBQUERY, v.v.).table: Bảng được truy vấn.type: Cách truy cập dữ liệu (ALL, INDEX, RANGE, v.v.).possible_keys: Các chỉ mục có thể dùng.key: Chỉ mục thực sự được dùng.rows: Số hàng dự kiến quét.Extra: Thông tin bổ sung (Using where, Using index, v.v.).
Tối ưu hóa với chỉ mục
Thêm chỉ mục để cải thiện hiệu suất:
CREATE INDEX idx_birthdate ON Students(Birthdate);
Sử dụng biến tạm (Temporary Tables) trong truy vấn
MySQL tự động tạo bảng tạm trong một số trường hợp (ví dụ: GROUP BY, ORDER BY). Để kiểm soát, bạn có thể tạo bảng tạm thủ công:
CREATE TEMPORARY TABLE TempStudents AS
SELECT StudentID, Name FROM Students WHERE Birthdate > '2000-01-01';
SELECT * FROM TempStudents;
DROP TEMPORARY TABLE TempStudents;
Tối ưu hóa bằng cách giảm tải
Sử dụng LIMIT để giảm số lượng bản ghi trả về:
SELECT * FROM Students WHERE Birthdate > '2000-01-01' LIMIT 100;
Full-Text Search (Tìm kiếm toàn văn)
MySQL hỗ trợ tìm kiếm toàn văn với chỉ mục FULLTEXT, chỉ khả dụng trên engine InnoDB hoặc MyISAM.
Tạo chỉ mục FULLTEXT
CREATE TABLE Articles (
ArticleID INT AUTO_INCREMENT PRIMARY KEY,
Title VARCHAR(255),
Content TEXT,
FULLTEXT(Title, Content)
);
Tìm kiếm với MATCH ... AGAINST
Tìm kiếm bài viết chứa từ "MySQL":
SELECT ArticleID, Title
FROM Articles
WHERE MATCH(Title, Content) AGAINST('MySQL' IN NATURAL LANGUAGE MODE);
IN NATURAL LANGUAGE MODE: Chế độ tìm kiếm tự nhiên.IN BOOLEAN MODE: Hỗ trợ toán tử (+, -, *, v.v.):SELECT ArticleID, Title FROM Articles WHERE MATCH(Title, Content) AGAINST('+MySQL -Tutorial' IN BOOLEAN MODE);
Xóa chỉ mục FULLTEXT
DROP INDEX idx_fulltext ON Articles;
ALTER TABLE Articles ADD FULLTEXT(Title, Content);
Lưu ý: FULLTEXT không hỗ trợ tất cả các kiểu dữ liệu và có giới hạn từ tối thiểu (mặc định là 4 ký tự).
Temporary Tables (Bảng tạm)
MySQL hỗ trợ bảng tạm tồn tại trong phiên làm việc (session).
Tạo bảng tạm
CREATE TEMPORARY TABLE TempEnrollments (
StudentID INT,
CourseID INT,
Fee INT
);
INSERT INTO TempEnrollments (StudentID, CourseID, Fee)
SELECT StudentID, CourseID, Fee FROM Enrollments WHERE Fee > 500;
SELECT * FROM TempEnrollments;
Xóa bảng tạm
Bảng tạm tự động bị xóa khi session kết thúc, hoặc xóa thủ công:
DROP TEMPORARY TABLE TempEnrollments;
Lưu ý: MySQL không có bảng tạm toàn cục như SQL Server (##Table).
Partitioning (Phân vùng)
MySQL hỗ trợ phân vùng bảng để quản lý dữ liệu lớn.
Tạo bảng phân vùng theo phạm vi (RANGE)
Phân vùng theo năm sinh:
CREATE TABLE StudentsPartitioned (
StudentID INT AUTO_INCREMENT,
Name VARCHAR(100),
Birthdate DATE,
PRIMARY KEY (StudentID, Birthdate)
)
PARTITION BY RANGE (YEAR(Birthdate)) (
PARTITION p0 VALUES LESS THAN (2000),
PARTITION p1 VALUES LESS THAN (2005),
PARTITION p2 VALUES LESS THAN (2010),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
Truy vấn phân vùng
SELECT * FROM StudentsPartitioned WHERE Birthdate < '2005-01-01';
Xóa phân vùng
ALTER TABLE StudentsPartitioned DROP PARTITION p0;
Lưu ý: Chỉ InnoDB và một số engine khác hỗ trợ phân vùng.
Event Scheduler (Lập lịch sự kiện)
MySQL hỗ trợ chạy tác vụ định kỳ bằng Event Scheduler.
Kích hoạt Event Scheduler
SET GLOBAL event_scheduler = ON;
Tạo Event
Xóa sinh viên chưa đăng ký khóa học nào mỗi ngày:
DELIMITER //
CREATE EVENT DeleteInactiveStudents
ON SCHEDULE EVERY 1 DAY
STARTS '2025-04-09 00:00:00'
DO
BEGIN
DELETE FROM Students
WHERE StudentID NOT IN (SELECT StudentID FROM Enrollments);
END //
DELIMITER ;
Xem danh sách Event
SHOW EVENTS FROM StudentManagementSystem;
Xóa Event
DROP EVENT DeleteInactiveStudents;
Backup và Restore nâng cao
Backup với mysqldump (nâng cao)
Sao lưu với điều kiện:
mysqldump -u root -p --where="Birthdate > '2000-01-01'" StudentManagementSystem Students > students_backup.sql
Restore với file lớn
Tăng giới hạn bộ nhớ khi khôi phục file lớn:
mysql -u root -p --max_allowed_packet=512M StudentManagementSystem < students_backup.sql
Performance Monitoring (Giám sát hiệu suất)
Xem truy vấn đang chạy
SHOW PROCESSLIST;
Kiểm tra trạng thái hệ thống
SHOW STATUS LIKE 'Threads%';
Profiling truy vấn
Kích hoạt profiling:
SET profiling = 1;
SELECT * FROM Students WHERE Birthdate > '2000-01-01';
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;
JSON Support (Hỗ trợ JSON)
MySQL hỗ trợ kiểu dữ liệu JSON từ phiên bản 5.7.
Tạo bảng với cột JSON
CREATE TABLE StudentDetails (
StudentID INT PRIMARY KEY,
Details JSON
);
INSERT INTO StudentDetails (StudentID, Details)
VALUES (1, '{"name": "Hoang Hiep", "scores": [85, 90, 88]}');
Truy vấn JSON
Lấy tên từ cột JSON:
SELECT StudentID, JSON_EXTRACT(Details, '$.name') AS Name
FROM StudentDetails;
Hoặc dùng toán tử ->:
SELECT StudentID, Details->'$.name' AS Name
FROM StudentDetails;
Cập nhật JSON
UPDATE StudentDetails
SET Details = JSON_SET(Details, '$.scores[0]', 95)
WHERE StudentID = 1;
Common Table Expressions (CTE)
MySQL hỗ trợ CTE từ phiên bản 8.0 (tương đương WITH trong SQL Server).
Sử dụng CTE
Lấy sinh viên và số khóa học đã đăng ký:
WITH EnrollmentCount AS (
SELECT s.StudentID, s.Name, COUNT(e.CourseID) AS CourseCount
FROM Students s
LEFT JOIN Enrollments e ON s.StudentID = e.StudentID
GROUP BY s.StudentID, s.Name
)
SELECT * FROM EnrollmentCount WHERE CourseCount > 2;
CTE đệ quy
Tạo danh sách số từ 1 đến 10:
WITH RECURSIVE Numbers AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM Numbers WHERE n < 10
)
SELECT * FROM Numbers;
Bài tập thực hành
Hệ thống quản lý sinh viên tại trường đại học EZSE
Cơ sở dữ liệu dưới đây được thiết kế để quản lý thông tin về sinh viên, giảng viên, ngành học, môn học, và các lớp học tại trường đại học EZSE. Hệ thống hỗ trợ các chức năng như đăng ký môn học, theo dõi điểm số, và quản lý các lớp học.
Câu lệnh SQL tạo Database và dữ liệu
-- Sử dụng database (MySQL không cần USE [master])
CREATE DATABASE IF NOT EXISTS EZSEStudentManagementSystem;
USE EZSEStudentManagementSystem;
-- Xóa database nếu đã tồn tại
DROP DATABASE IF EXISTS EZSEStudentManagementSystem;
CREATE DATABASE EZSEStudentManagementSystem;
USE EZSEStudentManagementSystem;
-- Xóa tất cả các bảng nếu tồn tại (MySQL không có sysdatabases, dùng DROP TABLE trực tiếp)
SET FOREIGN_KEY_CHECKS = 0; -- Tạm thời tắt kiểm tra khóa ngoại
-- Xóa các bảng theo thứ tự phù hợp để tránh lỗi khóa ngoại
DROP TABLE IF EXISTS Enrollments;
DROP TABLE IF EXISTS Classes;
DROP TABLE IF EXISTS MajorCourses;
DROP TABLE IF EXISTS Courses;
DROP TABLE IF EXISTS Teachers;
DROP TABLE IF EXISTS Students;
DROP TABLE IF EXISTS Majors;
SET FOREIGN_KEY_CHECKS = 1; -- Bật lại kiểm tra khóa ngoại
-- ---------------------------- Create table ----------------------------------
-- Bảng Ngành học (Major)
CREATE TABLE Majors (
MajorID INT AUTO_INCREMENT PRIMARY KEY,
MajorName VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
);
-- Bảng Sinh viên (Student)
CREATE TABLE Students (
StudentID INT AUTO_INCREMENT PRIMARY KEY,
FirstName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
LastName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
DateOfBirth DATE NOT NULL,
Gender VARCHAR(15) CHECK (Gender IN ('Male', 'Female', 'Other')),
Email VARCHAR(100) UNIQUE NOT NULL,
Phone VARCHAR(15) UNIQUE,
Address VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
EnrollmentDate DATE NOT NULL,
MajorID INT,
FOREIGN KEY (MajorID) REFERENCES Majors(MajorID)
);
-- Bảng Giảng viên (Teacher)
CREATE TABLE Teachers (
TeacherID INT AUTO_INCREMENT PRIMARY KEY,
FirstName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
LastName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
Email VARCHAR(100) UNIQUE NOT NULL,
Phone VARCHAR(15) UNIQUE,
Address VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
);
-- Bảng Môn học (Course)
CREATE TABLE Courses (
CourseID INT AUTO_INCREMENT PRIMARY KEY,
CourseName VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
Credits INT CHECK (Credits > 0)
);
-- Bảng Môn học theo ngành (MajorCourses)
CREATE TABLE MajorCourses (
MajorID INT,
CourseID INT,
PRIMARY KEY (MajorID, CourseID),
FOREIGN KEY (MajorID) REFERENCES Majors(MajorID),
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);
-- Bảng Lớp học (Class)
CREATE TABLE Classes (
ClassID INT AUTO_INCREMENT PRIMARY KEY,
CourseID INT,
TeacherID INT,
Semester VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
Year INT NOT NULL,
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID),
FOREIGN KEY (TeacherID) REFERENCES Teachers(TeacherID)
);
-- Bảng Đăng ký môn học (Enrollment)
CREATE TABLE Enrollments (
StudentID INT,
ClassID INT,
Attempt INT NOT NULL DEFAULT 1,
Grade DECIMAL(4,2) CHECK (Grade >= 0 AND Grade <= 10),
PRIMARY KEY (StudentID, ClassID, Attempt),
FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
FOREIGN KEY (ClassID) REFERENCES Classes(ClassID)
);
-- Chèn dữ liệu vào bảng Majors
INSERT INTO Majors (MajorName) VALUES
('Computer Science'), -- Khoa học máy tính
('Software Engineering'), -- Kỹ thuật phần mềm
('Information Security'); -- An toàn thông tin
-- Chèn dữ liệu vào bảng Courses
INSERT INTO Courses (CourseName, Credits) VALUES
('Data Structures & Algorithms', 3),
('Object-Oriented Programming', 3),
('Database Systems', 3),
('Computer Networks', 3),
('Web Development', 4),
('Agile Software Development', 3),
('Cyber Security', 3),
('Mobile Application Development', 3);
-- Chèn dữ liệu vào bảng Students
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID) VALUES
('Phạm', 'Gia Bảo', '2002-03-10', 'Male', '[email protected]', '0912345678', '12 Trần Phú, Hà Nội', '2021-09-01', 1),
('Nguyễn', 'Thảo Linh', '2003-07-22', 'Female', '[email protected]', '0987654321', '34 Lê Lợi, TP.HCM', '2021-09-01', 2),
('Trần', 'Quang Vinh', '2001-05-15', 'Male', '[email protected]', '0933123456', '56 Nguyễn Trãi, Đà Nẵng', '2020-09-01', 3),
('Lê', 'Minh Nhật', '2002-09-25', 'Male', '[email protected]', '0977890123', '78 Hai Bà Trưng, Hải Phòng', '2021-09-01', 1),
('Vũ', 'Hồng Ngọc', '2003-01-30', 'Female', '[email protected]', '0966543210', '90 Quang Trung, Cần Thơ', '2022-09-01', 2),
('Đỗ', 'Thanh Huyền', '2004-02-14', 'Female', '[email protected]', '0913456789', '15 Hùng Vương, Huế', '2022-09-01', 3),
('Hoàng', 'Đức Anh', '2003-11-05', 'Male', '[email protected]', '0934567890', '27 Nguyễn Huệ, Quy Nhơn', '2021-09-01', 1),
('Ngô', 'Khánh Duy', '2002-08-19', 'Male', '[email protected]', '0945678901', '39 Lê Đại Hành, Vinh', '2020-09-01', 2),
('Bùi', 'Thục Đoan', '2004-06-25', 'Female', '[email protected]', '0956789012', '51 Phạm Ngũ Lão, Nha Trang', '2023-09-01', 3),
('Phan', 'Tiến Đạt', '2003-04-12', 'Male', '[email protected]', '0967890123', '63 Nguyễn Văn Cừ, Đà Lạt', '2022-09-01', 1),
('Đặng', 'Mỹ Linh', '2004-09-08', 'Female', '[email protected]', '0978901234', '75 Trần Hưng Đạo, Hà Nội', '2023-09-01', 2),
('Võ', 'Hoàng Phúc', '2002-12-30', 'Male', '[email protected]', '0989012345', '87 Bạch Đằng, TP.HCM', '2020-09-01', 3),
('Trương', 'Ngọc Ánh', '2003-03-17', 'Female', '[email protected]', '0990123456', '99 Nguyễn Thị Minh Khai, Đà Nẵng', '2021-09-01', 1),
('Lý', 'Quốc Hưng', '2004-07-21', 'Male', '[email protected]', '0901234567', '111 Lê Lai, Hải Phòng', '2023-09-01', 2),
('Nguyễn', 'Văn Hùng', '2003-05-10', 'Male', '[email protected]', '0912345689', '123 Nguyễn Huệ, Hà Nội', '2022-09-01', 1),
('Trần', 'Thị Mai', '2004-08-15', 'Female', '[email protected]', '0987654332', '45 Lê Lợi, TP.HCM', '2023-09-01', 1),
('Lê', 'Quang Minh', '2002-11-20', 'Male', '[email protected]', '0933123467', '67 Trần Phú, Đà Nẵng', '2021-09-01', 1),
('Phạm', 'Hồng Phúc', '2003-02-25', 'Male', '[email protected]', '0977890134', '89 Hai Bà Trưng, Hải Phòng', '2022-09-01', 1),
('Vũ', 'Thị Lan', '2004-04-12', 'Female', '[email protected]', '0966543221', '101 Quang Trung, Cần Thơ', '2023-09-01', 2),
('Đỗ', 'Minh Tuấn', '2003-09-30', 'Male', '[email protected]', '0913456790', '23 Hùng Vương, Huế', '2022-09-01', 2),
('Hoàng', 'Ngọc Ánh', '2002-07-18', 'Female', '[email protected]', '0934567901', '34 Nguyễn Huệ, Quy Nhơn', '2021-09-01', 2),
('Ngô', 'Văn Long', '2003-12-05', 'Male', '[email protected]', '0945678912', '56 Lê Đại Hành, Vinh', '2022-09-01', 3),
('Bùi', 'Thị Hương', '2004-03-22', 'Female', '[email protected]', '0956789023', '78 Phạm Ngũ Lão, Nha Trang', '2023-09-01', 3),
('Phan', 'Quốc Anh', '2002-06-15', 'Male', '[email protected]', '0967890134', '90 Nguyễn Văn Cừ, Đà Lạt', '2021-09-01', 3);
-- Chèn dữ liệu vào bảng Teachers
INSERT INTO Teachers (FirstName, LastName, Email, Phone, Address) VALUES
('Hoàng', 'Văn Hiệp', '[email protected]', '0944123456', '101 Lý Thường Kiệt, Hà Nội'),
('Lê', 'Thu Hà', '[email protected]', '0933221122', '202 Phạm Văn Đồng, TP.HCM'),
('Hoàng', 'Việt Thắng', '[email protected]', '0922334455', '303 Trường Chinh, Đà Nẵng'),
('Nguyễn', 'Minh Tuấn', '[email protected]', '0911445566', '404 Nguyễn Văn Linh, Hải Phòng'),
('Phạm', 'Quốc Khánh', '[email protected]', '0902345678', '505 Điện Biên Phủ, Huế'),
('Vũ', 'Thị Duyên', '[email protected]', '0913567890', '606 Nguyễn Đình Chiểu, TP.HCM'),
('Trần', 'Đình Phong', '[email protected]', '0924678901', '707 Trần Phú, Hà Nội'),
('Đỗ', 'Ngọc Mai', '[email protected]', '0935789012', '808 Lê Hồng Phong, Đà Nẵng'),
('Bùi', 'Gia Bảo', '[email protected]', '0946890123', '909 Nguyễn Trãi, Hải Phòng'),
('Ngô', 'Thị Lan Anh', '[email protected]', '0957901234', '1010 Phạm Hùng, Cần Thơ');
-- Chèn dữ liệu vào bảng MajorCourses
INSERT INTO MajorCourses (MajorID, CourseID) VALUES
(1, 1), (1, 3), (1, 4), (1, 6),
(2, 2), (2, 5), (2, 6),
(3, 4), (3, 7), (3, 8);
-- Chèn dữ liệu vào bảng Classes
INSERT INTO Classes (CourseID, TeacherID, Semester, Year) VALUES
(1, 1, 'Spring', 2023),
(2, 2, 'Spring', 2023),
(3, 1, 'Summer', 2023),
(4, 3, 'Fall', 2023),
(5, 2, 'Spring', 2023),
(6, 4, 'Summer', 2023),
(7, 3, 'Fall', 2023),
(8, 5, 'Spring', 2023),
(1, 1, 'Spring', 2024),
(2, 2, 'Fall', 2024),
(3, 1, 'Spring', 2024),
(4, 3, 'Fall', 2024),
(5, 2, 'Summer', 2024),
(6, 4, 'Spring', 2024),
(7, 3, 'Fall', 2024),
(8, 5, 'Summer', 2024),
(1, 1, 'Fall', 2024),
(3, 6, 'Fall', 2024),
(1, 1, 'Spring', 2025),
(2, 2, 'Spring', 2025),
(3, 1, 'Summer', 2025),
(4, 3, 'Fall', 2025),
(5, 2, 'Spring', 2025),
(6, 4, 'Summer', 2025),
(7, 3, 'Fall', 2025),
(8, 5, 'Spring', 2025),
(2, 2, 'Fall', 2025),
(4, 3, 'Spring', 2025);
-- Chèn dữ liệu vào bảng Enrollments
INSERT INTO Enrollments (StudentID, ClassID, Attempt, Grade) VALUES
(1, 1, 1, 8.5),
(1, 9, 1, 7.2),
(1, 3, 1, 6.8),
(1, 4, 1, 5.5),
(1, 12, 2, 7.5),
(2, 2, 1, 8.0),
(2, 5, 1, 9.0),
(2, 6, 1, 6.0),
(2, 14, 1, 4.8),
(3, 4, 1, 7.5),
(3, 7, 1, 8.8),
(3, 8, 1, 9.2),
(4, 1, 1, 5.0),
(4, 9, 2, 7.2),
(4, 5, 1, 8.5),
(4, 6, 1, 9.1),
(5, 2, 1, 6.8),
(5, 3, 1, 5.5),
(5, 11, 2, 7.5),
(5, 8, 1, 8.3),
(6, 12, 1, 8.0),
(6, 15, 1, 7.8),
(6, 16, 1, 9.0),
(7, 9, 1, 7.5),
(7, 11, 1, 8.2),
(7, 12, 1, 6.5),
(8, 2, 1, 8.8),
(8, 5, 1, 7.0),
(8, 6, 1, 6.2),
(9, 20, 1, 8.5),
(9, 23, 1, 9.0),
(9, 16, 1, 7.8),
(10, 9, 1, 6.5),
(10, 11, 1, 7.0),
(10, 14, 1, 8.5),
(15, 10, 1, 7.8),
(19, 13, 1, 8.2),
(16, 17, 1, 6.5),
(20, 19, 1, 7.5),
(22, 24, 1, 8.5),
(21, 25, 1, 6.8),
(23, 26, 1, 7.2),
(24, 27, 1, 8.8);
Chuyển đổi chính:
- MySQL không có
[master]haydbo.sysdatabases, thay bằngCREATE DATABASE IF NOT EXISTSvàDROP DATABASE IF EXISTS. IDENTITY(1,1)được thay bằngAUTO_INCREMENT.NVARCHARđược thay bằngVARCHARvớiCHARACTER SET utf8mb4để hỗ trợ Unicode.- Tắt/bật kiểm tra khóa ngoại bằng
SET FOREIGN_KEY_CHECKSthay vìALTER DATABASE ... SET OFFLINE. - MySQL không hỗ trợ script động phức tạp như SQL Server để xóa ràng buộc, nên thay bằng cách xóa bảng trực tiếp theo thứ tự tránh vi phạm khóa ngoại.
Các bảng trong Database
1. Bảng Majors (Ngành học)
Bảng này lưu thông tin về các ngành học tại trường.
CREATE TABLE Majors (
MajorID INT AUTO_INCREMENT PRIMARY KEY,
MajorName VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
);
- MajorID: Mã ngành học, là khóa chính, tự động tăng.
- MajorName: Tên ngành học.
2. Bảng Students (Sinh viên)
Bảng này lưu trữ thông tin về sinh viên trong hệ thống.
CREATE TABLE Students (
StudentID INT AUTO_INCREMENT PRIMARY KEY,
FirstName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
LastName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
DateOfBirth DATE NOT NULL,
Gender VARCHAR(15) CHECK (Gender IN ('Male', 'Female', 'Other')),
Email VARCHAR(100) UNIQUE NOT NULL,
Phone VARCHAR(15) UNIQUE,
Address VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
EnrollmentDate DATE NOT NULL,
MajorID INT,
FOREIGN KEY (MajorID) REFERENCES Majors(MajorID)
);
- StudentID: Mã sinh viên, là khóa chính, tự động tăng.
- FirstName, LastName: Tên và họ của sinh viên.
- DateOfBirth: Ngày sinh.
- Gender: Giới tính (Male, Female, Other).
- Email: Email của sinh viên, duy nhất.
- Phone: Số điện thoại, duy nhất.
- Address: Địa chỉ của sinh viên.
- EnrollmentDate: Ngày nhập học.
- MajorID: Mã ngành học của sinh viên, tham chiếu đến bảng
Majors.
3. Bảng Teachers (Giảng viên)
Bảng này lưu trữ thông tin về các giảng viên.
CREATE TABLE Teachers (
TeacherID INT AUTO_INCREMENT PRIMARY KEY,
FirstName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
LastName VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
Email VARCHAR(100) UNIQUE NOT NULL,
Phone VARCHAR(15) UNIQUE,
Address VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
);
- TeacherID: Mã giảng viên, là khóa chính, tự động tăng.
- FirstName, LastName: Tên và họ của giảng viên.
- Email: Email của giảng viên, duy nhất.
- Phone: Số điện thoại, duy nhất.
- Address: Địa chỉ của giảng viên.
4. Bảng Courses (Môn học)
Bảng này lưu thông tin về các môn học.
CREATE TABLE Courses (
CourseID INT AUTO_INCREMENT PRIMARY KEY,
CourseName VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
Credits INT CHECK (Credits > 0)
);
- CourseID: Mã môn học, là khóa chính, tự động tăng.
- CourseName: Tên môn học.
- Credits: Số tín chỉ của môn học.
5. Bảng MajorCourses (Môn học theo ngành)
Bảng này liên kết các ngành học với các môn học cố định mà sinh viên phải học.
CREATE TABLE MajorCourses (
MajorID INT,
CourseID INT,
PRIMARY KEY (MajorID, CourseID),
FOREIGN KEY (MajorID) REFERENCES Majors(MajorID),
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID)
);
- MajorID: Mã ngành học.
- CourseID: Mã môn học.
- Mối quan hệ giữa ngành học và môn học là một-nhiều.
6. Bảng Classes (Lớp học)
Bảng này lưu thông tin về các lớp học, bao gồm thông tin môn học, giảng viên, và học kỳ.
CREATE TABLE Classes (
ClassID INT AUTO_INCREMENT PRIMARY KEY,
CourseID INT,
TeacherID INT,
Semester VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
Year INT NOT NULL,
FOREIGN KEY (CourseID) REFERENCES Courses(CourseID),
FOREIGN KEY (TeacherID) REFERENCES Teachers(TeacherID)
);
- ClassID: Mã lớp học, là khóa chính, tự động tăng.
- CourseID: Mã môn học, tham chiếu đến bảng
Courses. - TeacherID: Mã giảng viên, tham chiếu đến bảng
Teachers. - Semester: Học kỳ.
- Year: Năm học.
7. Bảng Enrollments (Đăng ký môn học)
Bảng này lưu trữ thông tin về việc sinh viên đăng ký học các lớp, bao gồm thông tin về lần đăng ký (Attempt) và điểm số của sinh viên.
CREATE TABLE Enrollments (
StudentID INT,
ClassID INT,
Attempt INT NOT NULL DEFAULT 1,
Grade DECIMAL(4,2) CHECK (Grade >= 0 AND Grade <= 10),
PRIMARY KEY (StudentID, ClassID, Attempt),
FOREIGN KEY (StudentID) REFERENCES Students(StudentID),
FOREIGN KEY (ClassID) REFERENCES Classes(ClassID)
);
- StudentID: Mã sinh viên, tham chiếu đến bảng
Students. - ClassID: Mã lớp học, tham chiếu đến bảng
Classes. - Attempt: Số lần sinh viên đăng ký học môn này (ví dụ, lần đầu hoặc học lại, cải thiện điểm).
- Grade: Điểm số của sinh viên trong lớp học.
Chuyển đổi chính:
IDENTITY(1,1)được thay bằngAUTO_INCREMENT.NVARCHARđược thay bằngVARCHARvớiCHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ciđể hỗ trợ Unicode (tương đươngNVARCHARtrong SQL Server).- MySQL yêu cầu định nghĩa khóa ngoại trực tiếp trong khai báo cột hoặc sau cùng, không cần
GOphân tách. CHECKtrong MySQL được hỗ trợ từ phiên bản 8.0.16 với engine InnoDB; nếu dùng phiên bản cũ hơn, bạn cần bỏ ràng buộcCHECKhoặc dùng trigger để thay thế.
Đề thực hành (Practical Exam)
Câu hỏi 1
Viết truy vấn để hiển thị tất cả các giảng viên.

Câu hỏi 2
Viết truy vấn để hiển thị tất cả các sinh viên thuộc các ngành có MajorID = 2 hoặc MajorID = 3.

Câu hỏi 3
Viết truy vấn để lấy thông tin các lớp học mà giảng viên có họ là "Hoàng" và tên là "Văn Hiệp" phụ trách.
Hiển thị các cột như phía dưới.

Câu hỏi 4
Viết truy vấn để lấy danh sách sinh viên đã đăng ký ít nhất một trong các lớp thuộc các môn học "Data Structures & Algorithms", "Database Systems", hoặc "Web Development".
Hiển thị các cột như phía dưới, không hiển thị kết quả trùng nhau.
Kết quả sắp xếp theo CourseID tăng dần, sau đó theo StudentID tăng dần.

Câu hỏi 5
Viết truy vấn để hiển thị số lượng sinh viên đã đăng ký vào các khóa học trong năm 2025.
Hiển thị các cột như phía dưới.
Kết quả sắp xếp theo NumberOfEnrollments giảm dần, sau đó theo CourseID tăng dần.

Câu hỏi 6
Viết truy vấn để tìm các giảng viên có số lượng lớp dạy nhiều nhất trong từng năm.
Hiển thị các cột như phía dưới.
Kết quả sắp xếp theo Year tăng dần, sau đó theo TeacherID tăng dần.

Câu hỏi 7
Viết truy vấn để đếm số sinh viên duy nhất đã đăng ký môn học theo từng năm của ngành "Computer Science" và "Software Engineering".
Hiển thị các cột như phía dưới.

Câu hỏi 8
Tạo function GetNumberOfStudentsByCourseID để tính số lượng sinh viên duy nhất đã đăng ký một môn học cụ thể theo CourseID.
Sau khi viết xong, hãy kiểm tra lại function bằng câu lệnh SQL dưới.
SELECT CourseID, CourseName, GetNumberOfStudentsByCourseID(CourseID) AS NumberOfStudents
FROM Courses
WHERE CourseID IN (1, 2, 3, 4); -- Kiểm tra với một số CourseID cụ thể

Câu hỏi 9
Tạo trigger để xử lý dữ liệu khi thêm sinh viên mới vào bảng Students có tên trg_InsteadOfInsertStudent đảm bảo hai điều kiện:
- Nếu
Emailkhông chứa ký tự "@", tự động thêm@ezse.netvào cuối. - Nếu
MajorIDbị NULL, tự động đặtMajorIDthành ID của ngành "Software Engineering".
Sau khi viết xong, hãy kiểm tra lại trigger bằng câu SQL lệnh dưới.
-- Trường hợp thiếu @ -> Tự động thêm @ezse.net, MajorID có giá trị nên giữ nguyên
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES ('Nguyễn', 'Văn A', '2002-05-10', 'Male', 'nguyenvana', '0734567890', 'Hà Nội', '2024-04-02', 3);
-- Trường hợp thiếu MajorID -> Tự động gán MajorID của ngành Software Engineering
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES ('Trần', 'Thị B', '2001-08-20', 'Female', '[email protected]', '0678123456', 'HCM', '2024-04-02', NULL);
-- Kiểm tra kết quả
SELECT StudentID, FirstName, LastName, Email, MajorID FROM Students;
-- Chú ý: Vì Phone là Unique, khi test nhiều lần, bạn hãy thay đổi số điện thoại.

Câu hỏi 10
Xóa sinh viên có StudentID = 3 và StudentID = 5 khỏi bảng Students, đồng thời xóa các hàng liên quan trong bảng Enrollments.
Đáp án đề thực hành
Câu hỏi 1
Viết truy vấn để hiển thị tất cả các giảng viên.

Đáp án:
SELECT *
FROM Teachers;
Câu hỏi 2
Viết truy vấn để hiển thị tất cả các sinh viên thuộc các ngành có MajorID = 2 hoặc MajorID = 3.

Đáp án:
SELECT *
FROM Students
WHERE MajorID IN (2, 3);
Câu hỏi 3
Viết truy vấn để lấy thông tin các lớp học mà giảng viên có họ là "Hoàng" và tên là "Văn Hiệp" phụ trách.
Hiển thị các cột như phía dưới.

Đáp án:
SELECT C.ClassID, C.CourseID, CO.CourseName, C.Semester, C.Year, T.TeacherID, T.FirstName, T.LastName
FROM Classes C
JOIN Teachers T ON C.TeacherID = T.TeacherID
JOIN Courses CO ON C.CourseID = CO.CourseID
WHERE T.FirstName = 'Hoàng' AND T.LastName = 'Văn Hiệp';
Câu hỏi 4
Viết truy vấn để lấy danh sách sinh viên đã đăng ký ít nhất một trong các lớp thuộc các môn học "Data Structures & Algorithms", "Database Systems", hoặc "Web Development".
Hiển thị các cột như phía dưới, không hiển thị kết quả trùng nhau.
Kết quả sắp xếp theo CourseID tăng dần, sau đó theo StudentID tăng dần.

Đáp án:
SELECT DISTINCT CO.CourseID, CO.CourseName, S.StudentID, CONCAT(S.FirstName, ' ', S.LastName) AS StudentFullName, S.DateOfBirth
FROM Students S
JOIN Enrollments E ON S.StudentID = E.StudentID
JOIN Classes C ON E.ClassID = C.ClassID
JOIN Courses CO ON C.CourseID = CO.CourseID
WHERE CO.CourseName IN ('Data Structures & Algorithms', 'Database Systems', 'Web Development')
ORDER BY CO.CourseID, S.StudentID;
Chuyển đổi:
- MySQL không có
CONCATtrong SQL Server dạng mặc định, nhưngCONCAThoạt động tốt trong MySQL.
Câu hỏi 5
Viết truy vấn để hiển thị số lượng sinh viên đã đăng ký vào các khóa học trong năm 2025.
Hiển thị các cột như phía dưới.
Kết quả sắp xếp theo NumberOfEnrollments giảm dần, sau đó theo CourseID tăng dần.

Đáp án:
SELECT C.CourseID, C.CourseName,
COUNT(E.StudentID) AS NumberOfEnrollments
FROM Courses C
LEFT JOIN Classes Cl ON C.CourseID = Cl.CourseID
LEFT JOIN Enrollments E ON Cl.ClassID = E.ClassID
WHERE Cl.Year = 2025 OR Cl.Year IS NULL
GROUP BY C.CourseID, C.CourseName
ORDER BY NumberOfEnrollments DESC, C.CourseID ASC;
Chuyển đổi:
- MySQL không yêu cầu
ANDtrongLEFT JOIN, thay bằngWHEREđể lọc năm 2025. - Thêm
OR Cl.Year IS NULLđể đảm bảo các khóa học không có lớp nào trong năm 2025 vẫn xuất hiện với số lượng 0.
Câu hỏi 6
Viết truy vấn để tìm các giảng viên có số lượng lớp dạy nhiều nhất trong từng năm.
Hiển thị các cột như phía dưới.
Kết quả sắp xếp theo Year tăng dần, sau đó theo TeacherID tăng dần.

Đáp án:
WITH TeacherClasses AS (
SELECT
c.Year,
t.TeacherID,
CONCAT(t.FirstName, ' ', t.LastName) AS TeacherFullName,
COUNT(c.ClassID) AS NumberOfClasses
FROM Teachers t
LEFT JOIN Classes c ON t.TeacherID = c.TeacherID
GROUP BY c.Year, t.TeacherID, t.FirstName, t.LastName
),
MaxClasses AS (
SELECT
Year,
MAX(NumberOfClasses) AS MaxClasses
FROM TeacherClasses
GROUP BY Year
)
SELECT
tc.Year,
tc.TeacherID,
tc.TeacherFullName,
tc.NumberOfClasses
FROM TeacherClasses tc
JOIN MaxClasses mc ON tc.Year = mc.Year AND tc.NumberOfClasses = mc.MaxClasses
ORDER BY tc.Year, tc.TeacherID;
Chuyển đổi:
- MySQL hỗ trợ CTE (
WITH) từ phiên bản 8.0. Nếu dùng phiên bản cũ hơn, cần thay bằng subquery.
Câu hỏi 7
Viết truy vấn để đếm số sinh viên duy nhất đã đăng ký môn học theo từng năm của ngành "Computer Science" và "Software Engineering".
Hiển thị các cột như phía dưới.

Đáp án:
WITH StudentEnrollments AS (
SELECT
m.MajorID,
m.MajorName,
c.Year,
COUNT(DISTINCT e.StudentID) AS TotalStudents
FROM Enrollments e
JOIN Classes c ON e.ClassID = c.ClassID
JOIN Students s ON e.StudentID = s.StudentID
JOIN Majors m ON s.MajorID = m.MajorID
WHERE m.MajorName IN ('Computer Science', 'Software Engineering')
GROUP BY m.MajorID, m.MajorName, c.Year
),
MajorsCTE AS (
SELECT MajorID, MajorName
FROM Majors
WHERE MajorName IN ('Computer Science', 'Software Engineering')
),
Years AS (
SELECT DISTINCT Year FROM Classes
),
AllCombinations AS (
SELECT y.Year, m.MajorID, m.MajorName
FROM MajorsCTE m CROSS JOIN Years y
)
SELECT
ac.Year,
ac.MajorID,
ac.MajorName,
COALESCE(se.TotalStudents, 0) AS TotalStudents
FROM AllCombinations ac
LEFT JOIN StudentEnrollments se
ON ac.MajorID = se.MajorID
AND ac.Year = se.Year
ORDER BY ac.Year, ac.MajorID;
Chuyển đổi:
- MySQL dùng
COALESCEthay vìISNULL. - CTE được giữ nguyên, nhưng nếu dùng MySQL < 8.0, cần thay bằng subquery.
Câu hỏi 8
Tạo function GetNumberOfStudentsByCourseID để tính số lượng sinh viên duy nhất đã đăng ký một môn học cụ thể theo CourseID.
Sau khi viết xong, hãy kiểm tra lại function bằng câu lệnh SQL dưới.
SELECT CourseID, CourseName, GetNumberOfStudentsByCourseID(CourseID) AS NumberOfStudents
FROM Courses
WHERE CourseID IN (1, 2, 3, 4); -- Kiểm tra với một số CourseID cụ thể

Đáp án:
DELIMITER //
CREATE FUNCTION GetNumberOfStudentsByCourseID(p_CourseID INT)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE NumberOfStudents INT;
SELECT COUNT(DISTINCT e.StudentID) INTO NumberOfStudents
FROM Enrollments e
JOIN Classes c ON e.ClassID = c.ClassID
WHERE c.CourseID = p_CourseID;
RETURN NumberOfStudents;
END //
DELIMITER ;
-- Kiểm tra function
SELECT CourseID, CourseName, GetNumberOfStudentsByCourseID(CourseID) AS NumberOfStudents
FROM Courses
WHERE CourseID IN (1, 2, 3, 4);
Chuyển đổi:
- MySQL không dùng
@cho tham số như SQL Server, thay bằng tên tham số trực tiếp (p_CourseID). - Thêm
DELIMITERđể định nghĩa function. - Thêm
DETERMINISTICđể chỉ hàm luôn trả về cùng kết quả với cùng đầu vào. - Biến trong MySQL được khai báo bằng
DECLAREvà gán giá trị bằngINTO.
Câu hỏi 9
Tạo trigger để xử lý dữ liệu khi thêm sinh viên mới vào bảng Students có tên trg_InsteadOfInsertStudent đảm bảo hai điều kiện:
- Nếu
Emailkhông chứa ký tự "@", tự động thêm@ezse.netvào cuối. - Nếu
MajorIDbị NULL, tự động đặtMajorIDthành ID của ngành "Software Engineering".
Sau khi viết xong, hãy kiểm tra lại trigger bằng câu SQL lệnh dưới.
-- Trường hợp thiếu @ -> Tự động thêm @ezse.net, MajorID có giá trị nên giữ nguyên
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES ('Nguyễn', 'Văn A', '2002-05-10', 'Male', 'nguyenvana', '0734567890', 'Hà Nội', '2024-04-02', 3);
-- Trường hợp thiếu MajorID -> Tự động gán MajorID của ngành Software Engineering
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES ('Trần', 'Thị B', '2001-08-20', 'Female', '[email protected]', '0678123456', 'HCM', '2024-04-02', NULL);
-- Kiểm tra kết quả
SELECT StudentID, FirstName, LastName, Email, MajorID FROM Students;
-- Chú ý: Vì Phone là Unique, khi test nhiều lần, bạn hãy thay đổi số điện thoại.

Đáp án:
DELIMITER //
CREATE TRIGGER trg_InsteadOfInsertStudent
BEFORE INSERT ON Students
FOR EACH ROW
BEGIN
DECLARE SoftwareEngineeringID INT;
-- Lấy MajorID của ngành "Software Engineering"
SELECT MajorID INTO SoftwareEngineeringID
FROM Majors
WHERE MajorName = 'Software Engineering';
-- Xử lý Email: Nếu không chứa @ thì thêm @ezse.net
IF NEW.Email NOT LIKE '%@%' THEN
SET NEW.Email = CONCAT(NEW.Email, '@ezse.net');
END IF;
-- Xử lý MajorID: Nếu NULL thì gán MajorID của "Software Engineering"
IF NEW.MajorID IS NULL THEN
SET NEW.MajorID = SoftwareEngineeringID;
END IF;
END //
DELIMITER ;
-- Kiểm tra trigger
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES ('Nguyễn', 'Văn A', '2002-05-10', 'Male', 'nguyenvana', '0734567890', 'Hà Nội', '2024-04-02', 3);
INSERT INTO Students (FirstName, LastName, DateOfBirth, Gender, Email, Phone, Address, EnrollmentDate, MajorID)
VALUES ('Trần', 'Thị B', '2001-08-20', 'Female', '[email protected]', '0678123456', 'HCM', '2024-04-02', NULL);
SELECT StudentID, FirstName, LastName, Email, MajorID FROM Students;
Chuyển đổi:
- MySQL không hỗ trợ
INSTEAD OFtrigger, thay bằngBEFORE INSERT. - Sử dụng
NEW.column_nameđể truy cập giá trị mới. - Thêm
DELIMITERđể định nghĩa trigger. NOT LIKE '%@%'thay choNOT LIKE '%@%'trong SQL Server.ISNULLthay bằngIS NULL.CONCATthay cho+để nối chuỗi.
Câu hỏi 10
Xóa sinh viên có StudentID = 3 và StudentID = 5 khỏi bảng Students, đồng thời xóa các hàng liên quan trong bảng Enrollments.
Đáp án:
-- Xóa dữ liệu liên quan trong bảng Enrollments trước
DELETE FROM Enrollments
WHERE StudentID IN (3, 5);
-- Xóa sinh viên trong bảng Students
DELETE FROM Students
WHERE StudentID IN (3, 5);
Giải thích:
Xóa dữ liệu liên quan trong
Enrollmentstrước- Bảng
Enrollmentscó khóa ngoạiStudentIDtham chiếu đếnStudents. - Nếu không có
ON DELETE CASCADE, ta phải xóa các bản ghi liên quan trongEnrollmentstrước.
- Bảng
Xóa dữ liệu chính trong
Students- Sau khi xóa các bản ghi trong
Enrollments, ta có thể xóa sinh viên cóStudentID = 3vàStudentID = 5mà không vi phạm ràng buộc.
- Sau khi xóa các bản ghi trong
Trường hợp có ON DELETE CASCADE trên Enrollments.StudentID
Nếu khi tạo bảng Enrollments, ta đã thiết lập:
FOREIGN KEY (StudentID) REFERENCES Students(StudentID) ON DELETE CASCADE
thì chỉ cần chạy:
DELETE FROM Students WHERE StudentID IN (3, 5);
Các bản ghi trong Enrollments sẽ tự động bị xóa.
