1. Quản lý người dùng và quyền hạn
Để kiểm tra danh sách người dùng trong hệ thống MySQL:
USE mysql;
SELECT * FROM user;
Tạo người dùng mới với điều kiện truy cập từ máy chủ cụ thể hoặc bất kỳ đâu:
-- Người dùng 'cc' chỉ được phép đăng nhập từ localhost
CREATE USER 'cc'@'localhost' IDENTIFIED BY '1234';
-- Người dùng 'gg' có thể truy cập từ mọi nơi
CREATE USER 'gg'@'%' IDENTIFIED BY '1234';
Thay đổi mật khẩu cho tài khoản đã tồn tại:
ALTER USER 'cc'@'localhost' IDENTIFIED WITH mysql_native_password BY 'newpass';
Xóa người dùng:
DROP USER 'gg'@'%';
Phân quyền và thu hồi quyền
Kiểm tra quyền hiện tại của một người dùng:
SHOW GRANTS FOR 'cc'@'%';
Cấp quyền truy cập đầy đủ vào bảng table1 trong cơ sở dữ liệu testDb:
GRANT ALL ON testDb.table1 TO 'cc'@'%';
Thu hồi quyền:
REVOKE ALL ON testDb.table1 FROM 'cc'@'%';
2. Thứ tự thực thi câu lệnh SQL
Trình tự xử lý câu truy vấn SELECT như sau:
- FROM: Xác định bảng nguồn.
- WHERE: Lọc dữ liệu theo điều kiện.
- GROUP BY và HAVING: Nhóm và lọc nhóm.
- SELECT: Chọn cột trả về.
- ORDER BY: Sắp xếp kết quả.
- LIMIT: Giới hạn số lượng bản ghi.
Ví dụ sau sẽ gây lỗi vì dùng bí danh ở mệnh đề WHERE:
SELECT user_name AS un FROM users WHERE un = 'tom'; -- Sai!
Bí danh chỉ sử dụng được ở ORDER BY, không dùng được trong WHERE.
3. Các hàm tích hợp trong MySQL
3.1 Hàm chuỗi
| Hàm | Mô tả |
|---|---|
| CONCAT(s1,s2,...) | Nối các chuỗi thành một |
| UPPER(str) | Chuyển sang chữ in hoa |
| LOWER(str) | Chuyển sang chữ thường |
| LPAD(str,n,pad) | Đệm trái để đạt độ dài n |
| RPAD(str,n,pad) | Đệm phải để đạt độ dài n |
| TRIM(str) | Xóa khoảng trắng đầu/cuối |
| SUBSTRING(str,pos,len) | Cắt chuỗi từ vị trí pos, độ dài len |
Ví dụ:
SELECT SUBSTRING('Nguyen Van A', 9, 3); -- Kết quả: 'Van'
3.2 Hàm số học
| Hàm | Mô tả |
|---|---|
| CEIL(x) | Làm tròn lên |
| FLOOR(x) | Làm tròn xuống |
| ROUND(x,d) | Làm tròn đến d chữ số thập phân |
| RAND() | Sinh số ngẫu nhiên [0,1) |
| MOD(a,b) | Phép chia lấy dư |
3.3 Hàm ngày tháng
| Hàm | Mô tả |
|---|---|
| NOW() | Thời gian hiện tại (ngày + giờ) |
| CURDATE() | Chỉ ngày hiện tại |
| CURTIME() | Chỉ giờ hiện tại |
| YEAR(date) | Lấy năm từ giá trị ngày |
| MONTH(date) | Lấy tháng |
| DATEDIFF(d1,d2) | Số ngày giữa hai mốc thời gian |
Ví dụ: Tính số ngày làm việc của nhân viên
SELECT name, DATEDIFF(CURDATE(), join_date) AS days_worked
FROM employees ORDER BY days_worked DESC;
3.4 Hàm điều kiện
| Hàm | Mô tả |
|---|---|
| IF(condition, val1, val2) | Nếu đúng thì val1, sai thì val2 |
| IFNULL(val1,val2) | Nếu val1 NULL thì trả về val2 |
| CASE WHEN ... THEN ... ELSE ... END | Rẽ nhánh nhiều điều kiện |
Ví dụ phân loại độ tuổi:
SELECT name,
CASE
WHEN age < 25 THEN 'Tre'
WHEN age <= 40 THEN 'Thanh nien'
ELSE 'Trung nien'
END AS category
FROM staff;
4. Truy vấn đa bảng
4.1 Hiện tượng tích Descartes
Xảy ra khi không có điều kiện nối giữa các bảng:
SELECT e.name, d.dept_name FROM employees e, departments d; -- Tích Descartes!
Luôn cần điều kiện nối để tránh kết quả thừa:
SELECT e.name, d.dept_name FROM employees e, departments d WHERE e.dept_id = d.id;
4.2 Nội liên kết (INNER JOIN)
Có hai cách viết:
- Ẩn:
FROM a, b WHERE a.id = b.a_id - Hiện:
FROM a INNER JOIN b ON a.id = b.a_id
Khuyến khích dùng dạng hiện rõ ràng và tối ưu hơn.
4.3 Ngoại liên kết
Liên kết trái: Lấy toàn bộ bảng bên trái và phần giao:
SELECT e.name, d.dept_name
FROM employees e LEFT JOIN departments d ON e.dept_id = d.id;
Liên kết phải: Ngược lại với trên.
4.4 Tự liên kết
Dùng khi cần so sánh các dòng trong cùng một bảng:
-- Hiển thị nhân viên và tên quản lý của họ
SELECT emp.name AS employee, mgr.name AS manager
FROM employees emp LEFT JOIN employees mgr ON emp.manager_id = mgr.id;
4.5 Truy vấn con (Subquery)
Theo dạng kết quả:
- Scalar: Trả về một giá trị duy nhất.
- Column: Trả về một cột (nhiều hàng).
- Row: Trả về một dòng (nhiều cột).
- Table: Trả về bảng con.
Ví dụ dùng toán tử IN và ALL:
-- Những nhân viên có lương cao hơn tất cả nhân viên phòng Kế toán
SELECT * FROM employees
WHERE salary > ALL (
SELECT salary FROM employees WHERE dept_id IN (
SELECT id FROM departments WHERE name = 'Ke toan'
)
);
4.6 Gộp kết quả (UNION)
Kết hợp kết quả từ nhiều truy vấn:
SELECT name FROM active_users
UNION
SELECT name FROM inactive_users;
Dùng UNION ALL nếu muốn giữ trùng lặp. Hiệu suất thường tốt hơn dùng OR.
5. Giao dịch (Transaction)
Giao dịch đảm bảo tính toàn vẹn dữ liệu qua thuộc tính ACID. Có hai loại:
- Ẩn: Mỗi lệnh DML tự động commit (INSERT/UPDATE/DELETE).
- Hiện: Tự kiểm soát điểm bắt đầu và kết thúc.
Cú pháp giao dịch tường minh:
SET autocommit = 0;
START TRANSACTION;
-- Thực hiện các thao tác
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- Đặt điểm lưu (tùy chọn)
SAVEPOINT sp1;
-- Thành công → commit, lỗi → rollback
COMMIT;
-- hoặc ROLLBACK;
-- hoặc ROLLBACK TO sp1;
5.1 Vấn đề đồng thời
- Đọc bẩn (Dirty Read): Đọc dữ liệu chưa được commit.
- Không lặp lại đọc (Non-repeatable Read): Cùng câu truy vấn nhưng kết quả khác nhau do bị sửa giữa chừng.
- Ảo ảnh (Phantom Read): Một bản ghi không tồn tại khi tìm kiếm, nhưng lại xuất hiện khi thêm vào.
5.2 Mức độ cô lập
Xem mức hiện tại:
SELECT @@TRANSACTION_ISOLATION;
Thiết lập:
-- Toàn hệ thống
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Chỉ phiên hiện tại
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Các mức: READ UNCOMMITTED → READ COMMITTED → REPEATABLE READ → SERIALIZABLE (an toàn tăng dần, hiệu năng giảm).
5.3 Giải quyết xung đột ghi (Mất cập nhật)
Khóa bi quan (Pessimistic Locking): Giả định luôn xảy ra xung đột.
SELECT * FROM products WHERE id = 100 FOR UPDATE; -- Khóa hàng
Khóa lạc quan (Optimistic Locking): Cho phép thao tác, kiểm tra衝突 trước khi ghi.
Thêm cột version hoặc updated_at:
UPDATE products SET price = 200, version = version + 1
WHERE id = 100 AND version = 1; -- Nếu ảnh hưởng 0 dòng → có xung đột