MySQL Cơ bản: Truy vấn, Hàm, Liên kết Bảng và Giao dịch

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:

  1. FROM: Xác định bảng nguồn.
  2. WHERE: Lọc dữ liệu theo điều kiện.
  3. GROUP BY và HAVING: Nhóm và lọc nhóm.
  4. SELECT: Chọn cột trả về.
  5. ORDER BY: Sắp xếp kết quả.
  6. 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àmMô 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àmMô 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àmMô 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àmMô 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 ... ENDRẽ 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

Thẻ: mysql sql transaction JOIN subquery

Đăng vào ngày 3 tháng 10 lúc 12:39