Học SQL cơ bản: SELECT, JOIN, GROUP BY kèm ví dụ

SQL cơ bản cho người mới: truy vấn, lọc, JOIN, GROUP BY, index và bài tập với MySQL.

03 tháng 11, 2026 7 phút đọc

Học SQL cơ bản bắt đầu từ việc hiểu bảng dữ liệu, sau đó luyện các câu lệnh SELECT, WHERE, ORDER BY, JOIN, GROUP BY và các lệnh thêm, sửa, xoá dữ liệu. Bài viết này dùng một cơ sở dữ liệu mẫu về khoá học để minh hoạ từng câu lệnh, kèm bài tập tự luyện và giải thích về index, transaction để bạn dùng SQL hiệu quả trong công việc.

SQL là gì và ai cần học SQL?

SQL (Structured Query Language) là ngôn ngữ dùng để truy vấn và quản lý dữ liệu trong cơ sở dữ liệu quan hệ như MySQL, PostgreSQL, SQL Server, SQLite. Không chỉ lập trình viên, nhiều vị trí khác cũng dùng SQL hằng ngày:

  • Backend developer: thiết kế bảng, viết truy vấn cho API.
  • Data analyst: lấy và tổng hợp dữ liệu để làm báo cáo.
  • Tester, product, marketing: tự kiểm tra dữ liệu mà không phải nhờ lập trình viên.

Cú pháp SQL cơ bản khá giống nhau giữa các hệ quản trị; bài viết dùng MySQL cho ví dụ.

Chuẩn bị môi trường

  • Cài MySQL Community Server và công cụ giao diện như MySQL Workbench, DBeaver hoặc TablePlus.
  • Hoặc chạy nhanh bằng Docker: docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=secret mysql:8. Xem thêm bài Docker là gì.

Tạo cơ sở dữ liệu mẫu

CREATE DATABASE hoc_sql;
USE hoc_sql;

CREATE TABLE courses (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(200) NOT NULL,
  category VARCHAR(50),
  price INT
);

CREATE TABLE students (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100),
  city VARCHAR(50)
);

CREATE TABLE enrollments (
  id INT PRIMARY KEY AUTO_INCREMENT,
  student_id INT,
  course_id INT,
  enrolled_at DATE,
  FOREIGN KEY (student_id) REFERENCES students(id),
  FOREIGN KEY (course_id) REFERENCES courses(id)
);

INSERT INTO courses (name, category, price) VALUES
('Python cơ bản', 'backend', 29000),
('ReactJS', 'frontend', 49000),
('Docker', 'devops', 39000);

INSERT INTO students (name, city) VALUES
('An', 'Hà Nội'), ('Bình', 'Đà Nẵng'), ('Chi', 'TP.HCM');

INSERT INTO enrollments (student_id, course_id, enrolled_at) VALUES
(1, 1, '2026-10-01'), (1, 2, '2026-10-05'), (2, 1, '2026-10-07'), (3, 3, '2026-10-10');

Dữ liệu trên chỉ là ví dụ minh hoạ cho bài học.

Truy vấn với SELECT

SELECT * FROM courses;
SELECT name, price FROM courses;
SELECT name, price FROM courses WHERE price < 40000;
SELECT name FROM courses WHERE category IN ('backend', 'devops');
SELECT name FROM courses WHERE name LIKE '%React%';
SELECT name, price FROM courses ORDER BY price DESC LIMIT 2;
  • WHERE lọc dòng theo điều kiện; kết hợp với AND, OR, BETWEEN, IN, LIKE, IS NULL.
  • ORDER BY sắp xếp; LIMIT giới hạn số dòng (rất hữu ích khi phân trang).
  • Trong dự án thật, hãy chọn cột cụ thể thay vì SELECT *.

JOIN – kết hợp nhiều bảng

Dữ liệu được tách thành nhiều bảng để tránh lặp. JOIN ghép chúng lại khi truy vấn.

-- Học viên nào học khoá nào
SELECT s.name AS hoc_vien, c.name AS khoa_hoc, e.enrolled_at
FROM enrollments e
JOIN students s ON s.id = e.student_id
JOIN courses c ON c.id = e.course_id;

-- Khoá học nào chưa có ai đăng ký
SELECT c.name
FROM courses c
LEFT JOIN enrollments e ON e.course_id = c.id
WHERE e.id IS NULL;
Loại JOINKết quả
INNER JOINChỉ các dòng khớp ở cả hai bảng
LEFT JOINTất cả dòng bảng trái, bảng phải không khớp thì là NULL
RIGHT JOINNgược lại với LEFT JOIN
FULL JOINTất cả dòng hai bảng (MySQL không hỗ trợ trực tiếp, dùng UNION)

GROUP BY và hàm tổng hợp

-- Số học viên của mỗi khoá
SELECT c.name, COUNT(e.id) AS so_hoc_vien
FROM courses c
LEFT JOIN enrollments e ON e.course_id = c.id
GROUP BY c.id, c.name
ORDER BY so_hoc_vien DESC;

-- Chỉ lấy khoá có từ 2 học viên trở lên
SELECT course_id, COUNT(*) AS so_hoc_vien
FROM enrollments
GROUP BY course_id
HAVING COUNT(*) >= 2;

Các hàm tổng hợp hay dùng: COUNT, SUM, AVG, MIN, MAX. Ghi nhớ: WHERE lọc trước khi nhóm, HAVING lọc sau khi nhóm.

Thêm, sửa, xoá dữ liệu

INSERT INTO students (name, city) VALUES ('Dũng', 'Cần Thơ');
UPDATE courses SET price = 35000 WHERE id = 3;
DELETE FROM enrollments WHERE id = 4;

Cảnh báo: luôn kiểm tra điều kiện WHERE trước khi chạy UPDATE hoặc DELETE. Thiếu WHERE sẽ thay đổi toàn bộ bảng. Mẹo an toàn: chạy SELECT với cùng điều kiện trước để xem những dòng sẽ bị ảnh hưởng.

Index – tăng tốc truy vấn

Index giống mục lục của cuốn sách, giúp database tìm dữ liệu nhanh mà không phải đọc toàn bộ bảng.

CREATE INDEX idx_enroll_course ON enrollments(course_id);
EXPLAIN SELECT * FROM enrollments WHERE course_id = 1;

Nên tạo index cho cột thường dùng trong WHERE, JOIN, ORDER BY. Tuy nhiên index làm chậm thao tác ghi và tốn dung lượng, vì vậy không nên tạo tràn lan. Dùng EXPLAIN để xem truy vấn có dùng index hay không.

Transaction – đảm bảo toàn vẹn dữ liệu

START TRANSACTION;
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
UPDATE accounts SET balance = balance + 100000 WHERE id = 2;
COMMIT;  -- hoặc ROLLBACK nếu có lỗi

Transaction đảm bảo một nhóm thao tác hoặc cùng thành công, hoặc cùng bị huỷ. Đây là nền tảng của các tính năng như thanh toán, chuyển tiền, đặt chỗ.

Bài tập tự luyện

  1. Liệt kê tên học viên ở Hà Nội và các khoá họ đã đăng ký.
  2. Tính tổng doanh thu theo từng danh mục khoá học.
  3. Tìm học viên đăng ký nhiều khoá nhất.
  4. Đếm số lượt đăng ký theo từng tháng.
  5. Tìm các khoá có giá cao hơn giá trung bình (gợi ý: dùng subquery).

Sai lầm thường gặp

  • Nối chuỗi trực tiếp dữ liệu người dùng vào câu SQL, gây lỗ hổng SQL injection. Hãy dùng prepared statement hoặc ORM.
  • Quên điều kiện JOIN, sinh ra số dòng nhân chéo khổng lồ.
  • Chọn cột trong SELECT không có trong GROUP BY hoặc hàm tổng hợp.
  • Lưu ngày tháng dưới dạng chuỗi thay vì kiểu DATE/DATETIME.

Thiết kế bảng: những nguyên tắc cơ bản

Viết truy vấn tốt bắt đầu từ thiết kế bảng tốt. Một vài nguyên tắc giúp dữ liệu gọn, ít lỗi:

  • Mỗi bảng mô tả một loại đối tượng: học viên, khoá học, đăng ký. Không gộp nhiều loại vào một bảng.
  • Mỗi bảng có khoá chính (thường là id tự tăng hoặc UUID).
  • Không lặp dữ liệu: thay vì lưu tên khoá học trong bảng đăng ký, chỉ lưu course_id và JOIN khi cần. Đây là tinh thần của chuẩn hoá (normalization).
  • Dùng khoá ngoại để database tự đảm bảo không có đăng ký trỏ tới khoá học không tồn tại.
  • Chọn kiểu dữ liệu phù hợp: số tiền dùng INT hoặc DECIMAL (không dùng FLOAT), ngày dùng DATE, trạng thái có thể dùng ENUM hoặc bảng tra cứu.
  • Đặt ràng buộc: NOT NULL, UNIQUE (ví dụ email), CHECK để chặn dữ liệu sai ngay từ đầu.

Subquery và CTE

Khi truy vấn phức tạp, bạn có thể chia nhỏ bằng subquery hoặc CTE (WITH), giúp code dễ đọc hơn:

WITH so_luong AS (
  SELECT course_id, COUNT(*) AS so_hoc_vien
  FROM enrollments
  GROUP BY course_id
)
SELECT c.name, COALESCE(s.so_hoc_vien, 0) AS so_hoc_vien
FROM courses c
LEFT JOIN so_luong s ON s.course_id = c.id
WHERE c.price > (SELECT AVG(price) FROM courses);

CTE đặc biệt hữu ích cho báo cáo nhiều bước. MySQL 8 trở lên và PostgreSQL đều hỗ trợ CTE và window function (ví dụ ROW_NUMBER(), RANK()) cho các bài toán xếp hạng, so sánh theo kỳ.

Câu hỏi thường gặp

Học SQL mất bao lâu?

Các câu lệnh cơ bản có thể nắm trong vài tuần luyện tập. Thiết kế cơ sở dữ liệu tốt và tối ưu truy vấn cần thêm kinh nghiệm qua dự án thật.

Nên học MySQL hay PostgreSQL?

Cả hai đều phổ biến và có cú pháp SQL cơ bản tương tự. Học tốt một hệ thì chuyển sang hệ còn lại khá nhanh.

SQL có khó hơn lập trình không?

SQL thường dễ bắt đầu hơn vì có tính khai báo: bạn mô tả dữ liệu cần lấy thay vì viết từng bước xử lý.

Data analyst có cần học SQL không?

Có. SQL là một trong những kỹ năng cốt lõi để lấy và tổng hợp dữ liệu, thường được kết hợp với Python hoặc công cụ BI.

Kết luận

SQL là kỹ năng nền tảng cho cả lập trình viên lẫn người làm dữ liệu. Hãy luyện tập trên một cơ sở dữ liệu mẫu, viết truy vấn mỗi ngày và chú ý đến an toàn khi sửa, xoá dữ liệu. Để học có hệ thống từ thiết kế bảng đến tối ưu truy vấn, tham khảo khoá học MySQL từ cơ bản đến nâng cao; nếu đi theo hướng dữ liệu, xem thêm Data Science với Python và bài lộ trình học backend.

#Học#SQL#bản#SELECT,#JOIN,

Thấy hữu ích? Chia sẻ ngay!