SQL Nâng Cao – Học Xong Là Bóc Rank Data Analyst Luôn

· 3 phút đọc
SQL Nâng Cao – Học Xong Là Bóc Rank Data Analyst Luôn
Photo by Puru Raj / Unsplash
Junior dùng SQL để lấy data.
Senior dùng SQL để chữa lành nỗi đau truy vấn chậm.
Principal dùng SQL để “vẽ ra cái business model từ mấy cái bảng”.

Học xong bài này bạn có thể:

  • Viết subquery lồng như búp bê Nga
  • Dùng CTE đệ quy tìm cây phân cấp
  • Viết window function đẹp như thơ
  • Hiểu tại sao index không luôn giúp nhanh
  • Biết cách bắt bệnh query bằng EXPLAIN

🧬 Subquery – Cái gì trong cái gì?

👉 Subquery trong SELECT

SELECT
  name,
  (SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id) AS total_orders
FROM users;
Tính số order mỗi user ngay trong dòng, thay vì JOIN + GROUP BY.

👉 Subquery trong WHERE

SELECT *
FROM users
WHERE id IN (
  SELECT user_id
  FROM orders
  WHERE amount > 1000
);
Ai từng “vung tiền” là hiện!

🤯 Subquery trong FROM (hay dùng để build bảng tạm)

SELECT country, AVG(age)
FROM (
  SELECT * FROM users WHERE active = true
) AS active_users
GROUP BY country;

🔁 CTE – Common Table Expression

Dễ đọc hơn subquery, support đệ quy, cực kỳ mạnh!

🧹 CTE cơ bản

WITH active_users AS (
  SELECT * FROM users WHERE active = true
)
SELECT COUNT(*) FROM active_users;

🚀 CTE đệ quy – Truy vấn dạng cây (như comments, categories)

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, 1 AS level
  FROM categories
  WHERE parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id, ct.level + 1
  FROM categories c
  JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree;
Truy xuất cây phân cấp không cần vòng lặp trong app! Chỉ cần 1 query!

🔭 Window Function – Mỗi dòng đều có quyền được tổng hợp

📌 RANK, ROW_NUMBER, DENSE_RANK

SELECT
  name,
  age,
  RANK() OVER (ORDER BY age DESC) AS age_rank
FROM users;

🧮 SUM chạy theo cửa sổ

SELECT
  user_id,
  amount,
  SUM(amount) OVER (PARTITION BY user_id ORDER BY created_at) AS running_total
FROM orders;
Phù hợp cho dashboard, báo cáo thời gian, vẽ biểu đồ etc.

🧠 INDEX – Không phải lúc nào cũng nhanh

  • Tăng tốc độ truy vấn ở WHERE, JOIN, ORDER BY
  • Nhưng ghi dữ liệu sẽ chậm hơn
  • Có nhiều loại: B-tree, GIN, GiST, BRIN
CREATE INDEX idx_users_email ON users(email);

⚠️ Những case không nên index:

  • Trường có quá ít giá trị (gender, status) → selectivity thấp
  • Dữ liệu nhỏ quá → overhead tạo index lớn hơn lợi ích

🩺 EXPLAIN – Bắt bệnh query

EXPLAIN ANALYZE
SELECT * FROM orders WHERE amount > 100;
Cho bạn biết:
  • PostgreSQL dùng index hay full scan
  • Dự đoán số row
  • Thời gian thực tế

Ví dụ output (rút gọn):

Seq Scan on orders  (cost=0.00..12.25 rows=5 width=...)
Filter: (amount > 100)
Nếu thấy “Seq Scan” mà bảng lớn → Cần index đó bro.

🏆 Chiêu cuối: Materialized View

Cache kết quả query phức tạp, dùng cho dashboard, thống kê.
CREATE MATERIALIZED VIEW top_users AS
SELECT user_id, COUNT(*) AS total_orders
FROM orders
GROUP BY user_id;

-- Cập nhật view khi có data mới
REFRESH MATERIALIZED VIEW top_users;

🧩 Một số pattern “xịn” cần học

Kỹ thuật Dùng khi nào
EXISTS vs IN Kiểm tra tồn tại, tối ưu hơn với bảng lớn
LEFT JOIN + IS NULL Tìm dữ liệu "không có liên kết"
COALESCE() Gán giá trị mặc định nếu NULL
FILTER() WITH AGG Tổng hợp có điều kiện trong 1 dòng
CASE WHEN If/else trong SQL

🧠 Tổng kết

Mức độ Kỹ năng cần biết
Basic SELECT, INSERT, JOIN, WHERE, GROUP BY
Trung Subquery, CTE, Aggregate, HAVING, Index
Nâng Window function, CTE đệ quy, Performance tuning
Pro Materialized view, Partitioning, Explain plan
SQL không phải để học thuộc – mà là để xài đúng trong đúng thời điểm!

💬 Tấu hài kết bài

Dev 1: “Tao viết query nhanh cực. Chạy trong 0.002 giây.”
Dev 2: “Ờ nhưng trả về sai hết.”
Dev 1: “Vấn đề ở logic, không phải tốc độ.”

🤣🤣🤣


📦 Bonus tài nguyên học nâng cao

-- Và như thường lệ: kết thúc bằng 1 query "truyền cảm hứng"
SELECT 'SQL giỏi là backend có cơm ăn!' AS truth;

🫰