Skip to content

SQL Cheatsheet - Tất cả trong một 📖

Chào anh em! Đây là tài liệu tổng hợp tất cả các câu lệnh SQL quan trọng mà anh em cần biết. Mình đã chia thành các phần rõ ràng để anh em dễ tra cứu.


📌 Mục lục nhanh

PhầnNội dung
SELECTTruy vấn dữ liệu cơ bản
WHEREĐiều kiện lọc
JOINKết hợp bảng
GROUP BYNhóm dữ liệu
HAVINGLọc sau khi nhóm
ORDER BYSắp xếp kết quả
INSERT/UPDATE/DELETEThao tác dữ liệu

SELECT - Truy vấn dữ liệu

Cú pháp cơ bản

sql
-- Lấy tất cả các cột
SELECT * FROM users;

-- Lấy các cột cụ thể (KHUYẾN KHÍCH!)
SELECT id, name, email FROM users;

-- Đặt alias cho cột
SELECT 
    first_name AS "Họ",
    last_name AS "Tên",
    salary * 12 AS "Lương năm"
FROM employees;

-- Loại bỏ giá trị trùng
SELECT DISTINCT department FROM employees;

💡 Mẹo của HPN

LUÔN chỉ định cột cụ thể thay vì dùng SELECT *. Điều này giúp:

  1. Query nhanh hơn (ít data truyền tải)
  2. Code rõ ràng hơn
  3. Tránh lỗi khi schema thay đổi

WHERE - Điều kiện lọc

Các toán tử so sánh

Toán tửÝ nghĩaVí dụ
=BằngWHERE status = 'active'
<> hoặc !=KhácWHERE status <> 'deleted'
>, <Lớn/Nhỏ hơnWHERE age > 18
>=, <=Lớn/Nhỏ hơn hoặc bằngWHERE price <= 100
BETWEENTrong khoảngWHERE age BETWEEN 18 AND 65
INTrong danh sáchWHERE status IN ('active', 'pending')
LIKEPattern matchingWHERE name LIKE 'Nguyễn%'
IS NULLKiểm tra NULLWHERE phone IS NULL

Pattern Matching với LIKE

sql
-- Bắt đầu bằng "Nguyễn"
SELECT * FROM users WHERE name LIKE 'Nguyễn%';

-- Kết thúc bằng "@gmail.com"
SELECT * FROM users WHERE email LIKE '%@gmail.com';

-- Chứa "dev" ở bất kỳ đâu
SELECT * FROM users WHERE bio LIKE '%dev%';

-- Đúng 5 ký tự (mỗi _ là 1 ký tự)
SELECT * FROM products WHERE code LIKE '_____';

Kết hợp điều kiện

sql
-- AND: Cả 2 điều kiện đều đúng
SELECT * FROM products 
WHERE category = 'Electronics' 
  AND price < 1000;

-- OR: Ít nhất 1 điều kiện đúng
SELECT * FROM products 
WHERE category = 'Books' 
   OR category = 'Music';

-- NOT: Phủ định điều kiện
SELECT * FROM users 
WHERE NOT status = 'banned';

-- Kết hợp phức tạp (dùng ngoặc!)
SELECT * FROM orders 
WHERE status = 'completed'
  AND (total > 1000000 OR vip_customer = true);

⚠️ Lỗi thường gặp

Khi kết hợp ANDOR, LUÔN sử dụng ngoặc đơn để rõ ràng thứ tự ưu tiên!

sql
-- ❌ SAI: Logic có thể sai
WHERE a = 1 OR b = 2 AND c = 3

-- ✅ ĐÚNG: Rõ ràng logic
WHERE a = 1 OR (b = 2 AND c = 3)

JOIN - Kết hợp bảng

Minh họa các loại JOIN

Bảng A (users)          Bảng B (orders)
+----+--------+         +----+---------+--------+
| id | name   |         | id | user_id | total  |
+----+--------+         +----+---------+--------+
| 1  | Hùng   |         | 1  | 1       | 500k   |
| 2  | Lan    |         | 2  | 1       | 300k   |
| 3  | Minh   |         | 3  | 4       | 200k   | ← user_id=4 không tồn tại!
+----+--------+         +----+---------+--------+

INNER JOIN (Giao)

Chỉ lấy các bản ghi có khớp ở cả 2 bảng.

sql
-- Lấy users VÀ orders của họ
SELECT 
    u.name AS "Tên khách",
    o.id AS "Mã đơn",
    o.total AS "Tổng tiền"
FROM users u
INNER JOIN orders o ON u.id = o.user_id;

-- Kết quả: Chỉ có Hùng (có đơn hàng)
-- Minh không xuất hiện (không có đơn)
-- Order id=3 không xuất hiện (user không tồn tại)

LEFT JOIN (Bảng trái là chính)

Lấy TẤT CẢ từ bảng trái, khớp gì lấy từ bảng phải.

sql
SELECT 
    u.name AS "Tên khách",
    o.id AS "Mã đơn",
    COALESCE(o.total, 0) AS "Tổng tiền"
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

-- Kết quả:
-- Hùng | 1 | 500k
-- Hùng | 2 | 300k
-- Lan  | NULL | 0    ← Lan chưa có đơn
-- Minh | NULL | 0    ← Minh chưa có đơn

RIGHT JOIN (Bảng phải là chính)

sql
SELECT 
    u.name AS "Tên khách",
    o.id AS "Mã đơn"
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;

-- Kết quả:
-- Hùng | 1
-- Hùng | 2
-- NULL | 3    ← Order không có user hợp lệ

FULL OUTER JOIN (Hợp của 2 bảng)

sql
SELECT 
    u.name,
    o.id
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;

-- Kết quả: Tất cả từ cả 2 bảng, NULL nếu không khớp

📊 Tổng hợp các loại JOIN

JOIN TypeBảng tráiBảng phảiĐiều kiện
INNERChỉ khớpChỉ khớpPhải khớp cả 2
LEFTTất cảChỉ khớpBảng trái là master
RIGHTChỉ khớpTất cảBảng phải là master
FULLTất cảTất cảLấy hết, NULL nếu không khớp

GROUP BY - Nhóm dữ liệu

Cú pháp cơ bản

sql
-- Đếm số đơn hàng theo trạng thái
SELECT 
    status AS "Trạng thái",
    COUNT(*) AS "Số lượng"
FROM orders
GROUP BY status;

-- Tổng doanh thu theo tháng
SELECT 
    DATE_TRUNC('month', created_at) AS "Tháng",
    SUM(total) AS "Doanh thu"
FROM orders
WHERE status = 'completed'
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY "Tháng";

Các hàm Aggregate phổ biến

HàmÝ nghĩaVí dụ
COUNT(*)Đếm tổng số dòngCOUNT(*)
COUNT(column)Đếm giá trị non-NULLCOUNT(email)
SUM(column)TổngSUM(total)
AVG(column)Trung bìnhAVG(price)
MIN(column)Giá trị nhỏ nhấtMIN(created_at)
MAX(column)Giá trị lớn nhấtMAX(salary)

HAVING - Lọc sau khi nhóm

💡 WHERE vs HAVING

  • WHERE: Lọc TRƯỚC khi nhóm (lọc từng dòng)
  • HAVING: Lọc SAU khi nhóm (lọc kết quả aggregate)
sql
-- Tìm category có nhiều hơn 10 sản phẩm
SELECT 
    category,
    COUNT(*) AS product_count
FROM products
WHERE is_active = true          -- Lọc sản phẩm active TRƯỚC
GROUP BY category
HAVING COUNT(*) > 10;          -- Lọc nhóm SAU

-- Tìm user đã mua tổng > 10 triệu
SELECT 
    user_id,
    SUM(total) AS total_spent
FROM orders
WHERE status = 'completed'
GROUP BY user_id
HAVING SUM(total) > 10000000
ORDER BY total_spent DESC;

ORDER BY - Sắp xếp

sql
-- Sắp xếp tăng dần (mặc định)
SELECT * FROM products ORDER BY price;
SELECT * FROM products ORDER BY price ASC;

-- Sắp xếp giảm dần
SELECT * FROM products ORDER BY price DESC;

-- Sắp xếp nhiều cột
SELECT * FROM employees 
ORDER BY department ASC, salary DESC;

-- Sắp xếp theo vị trí cột (không khuyến khích)
SELECT name, price FROM products ORDER BY 2 DESC;

-- NULL đầu tiên hoặc cuối cùng
SELECT * FROM users ORDER BY phone NULLS LAST;

CRUD Operations

INSERT - Thêm dữ liệu

sql
-- Thêm 1 dòng
INSERT INTO users (name, email, created_at)
VALUES ('Nguyễn Văn A', 'a@email.com', NOW());

-- Thêm nhiều dòng
INSERT INTO products (name, price, category) VALUES
    ('iPhone 15', 25000000, 'Electronics'),
    ('MacBook Pro', 50000000, 'Electronics'),
    ('AirPods', 4000000, 'Electronics');

-- Insert từ SELECT
INSERT INTO archived_orders (id, user_id, total)
SELECT id, user_id, total 
FROM orders 
WHERE created_at < '2023-01-01';

UPDATE - Cập nhật dữ liệu

sql
-- Cập nhật 1 trường
UPDATE users SET status = 'inactive' WHERE id = 123;

-- Cập nhật nhiều trường
UPDATE products 
SET 
    price = price * 1.1,        -- Tăng 10%
    updated_at = NOW()
WHERE category = 'Electronics';

🚨 CẢNH BÁO NGHIÊM TRỌNG

KHÔNG BAO GIỜ chạy UPDATE hoặc DELETEKHÔNG CÓ WHERE!

sql
-- ❌ RẤT NGUY HIỂM - Sẽ update TẤT CẢ users!
UPDATE users SET status = 'deleted';

-- ✅ An toàn - Chỉ update user cụ thể
UPDATE users SET status = 'deleted' WHERE id = 123;

Mẹo an toàn: Luôn chạy SELECT với điều kiện tương tự TRƯỚC khi UPDATE/DELETE:

sql
-- Bước 1: Kiểm tra data sẽ bị ảnh hưởng
SELECT * FROM users WHERE status = 'inactive';

-- Bước 2: Nếu đúng, mới chạy DELETE
DELETE FROM users WHERE status = 'inactive';

DELETE - Xóa dữ liệu

sql
-- Xóa có điều kiện
DELETE FROM orders WHERE status = 'cancelled';

-- Xóa dựa trên subquery
DELETE FROM cart_items 
WHERE user_id IN (
    SELECT id FROM users WHERE status = 'deleted'
);

🎁 Bonus: Các hàm hữu ích

Xử lý NULL

sql
-- COALESCE: Thay NULL bằng giá trị mặc định
SELECT COALESCE(phone, 'Chưa có SĐT') FROM users;

-- NULLIF: Trả về NULL nếu 2 giá trị bằng nhau
SELECT NULLIF(discount, 0) FROM products;

Xử lý chuỗi

sql
-- Nối chuỗi
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;
SELECT first_name || ' ' || last_name AS full_name FROM users; -- PostgreSQL

-- Upper/Lower
SELECT UPPER(email), LOWER(name) FROM users;

-- Cắt khoảng trắng
SELECT TRIM(name) FROM users;

-- Substring
SELECT SUBSTRING(phone, 1, 4) AS prefix FROM users;

Xử lý ngày tháng

sql
-- Lấy ngày hiện tại
SELECT CURRENT_DATE, CURRENT_TIMESTAMP, NOW();

-- Extract thành phần
SELECT 
    EXTRACT(YEAR FROM created_at) AS year,
    EXTRACT(MONTH FROM created_at) AS month
FROM orders;

-- Format ngày (PostgreSQL)
SELECT TO_CHAR(created_at, 'DD/MM/YYYY HH24:MI') FROM orders;

📚 Tài liệu tham khảo


📖 Happy querying, anh em! 🚀