Trả lời nhanh: SQL (Structured Query Language) là ngôn ngữ truy vấn có cấu trúc, dùng để tạo bảng, thêm, sửa, xoá và lấy dữ liệu trong cơ sở dữ liệu quan hệ. Bạn mô tả cần dữ liệu gì, hệ quản trị tự tìm cách lấy. SQL thành chuẩn ANSI năm 1986, ISO năm 1987.
Điểm chính
- SQL là một ngôn ngữ, không phải phần mềm. MySQL, PostgreSQL, SQL Server, Oracle là các hệ quản trị cơ sở dữ liệu dùng SQL, mỗi hệ có “phương ngữ” riêng.
- Câu lệnh SQL chia thành bốn nhóm: DDL (định nghĩa cấu trúc), DML (thao tác dữ liệu, gồm SELECT), DCL (phân quyền) và TCL (giao dịch).
- Nắm SELECT, WHERE, JOIN, GROUP BY và thứ tự xử lý logic của câu lệnh là bạn đã tự trả lời được phần lớn câu hỏi báo cáo hằng ngày.
- Bài có sẵn script bảng mẫu chạy được trên SQLite và năm bài tập SELECT, JOIN kèm đáp án để bạn tự kiểm tra.
- Theo Stack Overflow Developer Survey 2025, 58,6% người trả lời có dùng SQL, nhỉnh hơn cả Python.
- SQL injection đứng thứ 2 trong CWE Top 25 năm 2025. Cách phòng thủ chính là truy vấn tham số hoá và cấp quyền tối thiểu, không phải “lọc ký tự lạ”.
Mục lục
- SQL là gì?
- SQL dùng để làm gì?
- SQL ra đời như thế nào?
- Một câu lệnh SQL được xử lý ra sao?
- Các nhóm lệnh SQL gồm những gì?
- Câu lệnh SQL cơ bản: học qua bảng mẫu
- NULL trong SQL: vì sao kết quả “thiếu dòng”?
- Phương ngữ SQL: MySQL, PostgreSQL, SQL Server, Oracle khác nhau ở đâu?
- SQL và NoSQL khác nhau thế nào?
- SQL cho người làm phân tích dữ liệu cần gì?
- SQL injection là gì và phòng thủ thế nào?
- Học SQL bắt đầu từ đâu?
- Câu hỏi thường gặp
SQL là gì?
SQL, viết tắt của Structured Query Language (ngôn ngữ truy vấn có cấu trúc), là ngôn ngữ chuẩn để làm việc với cơ sở dữ liệu quan hệ (relational database), nơi dữ liệu được lưu thành các bảng gồm hàng và cột. Với SQL, bạn tạo bảng, thêm, sửa, xoá dòng, phân quyền và quan trọng nhất là hỏi dữ liệu.
SQL mang tính khai báo (declarative). Bạn viết “cho tôi các đơn trên 90.000 đồng, xếp giảm dần”, không cần chỉ máy đọc file nào, theo thứ tự nào. Phần “làm thế nào” do hệ quản trị cơ sở dữ liệu (DBMS) tự quyết. Vì vậy, một câu SQL ngắn chạy đúng trên bảng vài trăm dòng lẫn vài trăm triệu dòng.
Về cách đọc, chuẩn ban đầu quy định đọc từng chữ cái “ét-kiu-eo”; nhiều người làm nghề vẫn đọc “si-quồ” (sequel) theo tên gọi đầu tiên. Cả hai đều được hiểu.
SQL khác MySQL, SQL Server thế nào?
SQL là ngôn ngữ. MySQL, PostgreSQL, Microsoft SQL Server, Oracle Database, SQLite là các phần mềm hệ quản trị cơ sở dữ liệu quan hệ (RDBMS) hiểu ngôn ngữ đó. Giống tiếng Việt ở các vùng miền: phần lớn câu chữ giống nhau, một số từ khác nhau. Nếu chưa rõ bảng, khoá chính hay DBMS là gì, hãy đọc trước bài cơ sở dữ liệu là gì và các loại DBMS.
SQL dùng để làm gì?
Hầu như mọi phần mềm có lưu dữ liệu đều có SQL chạy phía sau: phần mềm bán hàng, kế toán, ngân hàng lõi, website thương mại điện tử. Trong doanh nghiệp, SQL được dùng theo bốn cách:
- Xây ứng dụng: lập trình viên lưu và đọc dữ liệu cho ứng dụng. Kể cả khi SQL được sinh tự động qua thư viện ORM, vẫn cần hiểu SQL để sửa lỗi, tối ưu.
- Phân tích, báo cáo: lấy số doanh thu, tồn kho, tỷ lệ quay lại rồi đưa vào Excel hoặc công cụ BI.
- Quản trị dữ liệu: tạo cấu trúc, phân quyền, theo dõi hiệu năng.
- Đường ống dữ liệu: làm sạch, gộp và chuyển dữ liệu giữa các hệ thống.
Trong Stack Overflow Developer Survey 2025, 58,6% người trả lời dùng SQL, chỉ sau JavaScript (66%) và HTML/CSS (61,9%). Bốn cơ sở dữ liệu được dùng nhiều nhất, PostgreSQL (55,6%), MySQL (40,5%), SQLite (37,5%) và SQL Server (30,1%), đều dùng SQL.
SQL ra đời như thế nào?
Năm 1970, Edgar F. Codd, nhà nghiên cứu của IBM, công bố bài báo “A Relational Model of Data for Large Shared Data Banks” trên Communications of the ACM, đề xuất lưu dữ liệu thành bảng. Đầu thập niên 1970, Donald D. Chamberlin và Raymond F. Boyce tại IBM thiết kế ngôn ngữ truy vấn cho mô hình này, đặt tên SEQUEL (Structured English Query Language), dùng trong dự án thử nghiệm System R. Tên sau đó đổi thành SQL vì “SEQUEL” đã là nhãn hiệu của công ty Anh Hawker Siddeley Dynamics Engineering.
IBM không phải hãng đầu tiên bán sản phẩm dùng SQL. Tháng 6/1979, Relational Software, Inc. (nay là Oracle Corporation) phát hành Oracle V2. Theo trang lịch sử của IBM, DB2 ra mắt năm 1983. Viện Tiêu chuẩn Quốc gia Hoa Kỳ (ANSI) công nhận SQL là tiêu chuẩn năm 1986, ISO năm 1987. Chuẩn hiện mang mã ISO/IEC 9075, các phiên bản đáng nhớ:
| Phiên bản | Bổ sung đáng chú ý |
|---|---|
| SQL-86 / SQL-87 | Bản chuẩn đầu tiên của ANSI, rồi ISO |
| SQL-92 | Bản sửa đổi lớn, mở rộng mạnh ngôn ngữ |
| SQL:1999 | Truy vấn đệ quy, trigger, câu lệnh thủ tục |
| SQL:2003 | Hàm cửa sổ (window function), SQL/XML, cột tự tăng |
| SQL:2008 | Mệnh đề FETCH để giới hạn số dòng, TRUNCATE |
| SQL:2011 | Dữ liệu theo thời gian (temporal) |
| SQL:2016 | Thao tác JSON, khớp mẫu theo hàng |
| SQL:2023 | Kiểu dữ liệu JSON; truy vấn đồ thị thuộc tính (SQL/PGQ) |
Không RDBMS nào cài đủ toàn bộ chuẩn, và tính năng mới đến với mỗi hệ vào thời điểm khác nhau. Trước khi dùng, hãy đọc tài liệu của đúng hệ và đúng phiên bản bạn đang chạy.
Một câu lệnh SQL được xử lý ra sao?
Khi bạn chạy một câu SQL, hệ quản trị kiểm tra cú pháp và quyền, chọn kế hoạch thực thi rẻ nhất, rồi mới đọc dữ liệu. Hiểu điều này giúp bạn đọc thông báo lỗi và biết vì sao cùng câu hỏi, cách viết này nhanh còn cách kia chậm.
Hầu hết hệ quản trị cho xem kế hoạch thực thi bằng lệnh EXPLAIN (MySQL, PostgreSQL, SQLite) hoặc tính năng “Display Execution Plan” (SQL Server). Khi truy vấn chậm, đây là chỗ đầu tiên nên xem: có dùng chỉ mục (index) hay đang quét cả bảng.
Các nhóm lệnh SQL gồm những gì?
Câu lệnh SQL chia thành bốn nhóm. Một số tài liệu tách SELECT thành nhóm thứ năm là DQL (Data Query Language).
| Nhóm | Dùng để | Lệnh tiêu biểu |
|---|---|---|
| DDL (Data Definition Language) | Tạo, sửa, xoá cấu trúc: bảng, chỉ mục, view | CREATE, ALTER, DROP, TRUNCATE |
| DML (Data Manipulation Language) | Đọc, thêm, sửa, xoá dữ liệu | SELECT, INSERT, UPDATE, DELETE, MERGE |
| DCL (Data Control Language) | Cấp và thu hồi quyền | GRANT, REVOKE |
| TCL (Transaction Control Language) | Gom nhiều lệnh thành một giao dịch, xác nhận hoặc huỷ | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
-- DDL: tạo bảng
CREATE TABLE cua_hang (
ma_ch INT PRIMARY KEY,
ten VARCHAR(100) NOT NULL,
thanh_pho VARCHAR(50) NOT NULL
);
-- DML: thêm dòng
INSERT INTO cua_hang VALUES (4, 'Thủ Đức', 'Hồ Chí Minh');
-- DCL: tài khoản báo cáo chỉ được đọc
GRANT SELECT ON cua_hang TO bao_cao;
-- TCL: xác nhận thay đổi
COMMIT;
Lưu ý: ở Oracle và MySQL, lệnh DDL tự động xác nhận giao dịch nên không ROLLBACK được; PostgreSQL và SQL Server cho đặt phần lớn DDL trong giao dịch. Đừng chạy thử DROP hay ALTER trên dữ liệu thật với suy nghĩ “sai thì quay lại”.
Câu lệnh SQL cơ bản: học qua bảng mẫu
Ví dụ minh hoạ. Một chuỗi cà phê nhỏ có bảng cua_hang và bảng don_hang (trỏ về cửa hàng qua cột ma_ch). Cửa hàng Thủ Đức mới mở, chưa có đơn.
| ma_ch | ten | thanh_pho |
|---|---|---|
| 1 | Quận 1 | Hồ Chí Minh |
| 2 | Hoàn Kiếm | Hà Nội |
| 3 | Hải Châu | Đà Nẵng |
| 4 | Thủ Đức | Hồ Chí Minh |
| ma_don | ma_ch | ngay | tong_tien |
|---|---|---|---|
| 101 | 1 | 2026-09-01 | 120000 |
| 102 | 1 | 2026-09-01 | 85000 |
| 103 | 2 | 2026-09-02 | 150000 |
| 104 | 3 | 2026-09-02 | 60000 |
| 105 | 2 | 2026-09-03 | 95000 |
| 106 | 1 | 2026-09-03 | 70000 |
Thực hành ngay: dựng bảng mẫu trên máy bằng SQLite
Đọc SQL không đủ, bạn cần gõ và nhìn kết quả. SQLite là cách nhanh nhất vì không cần cài máy chủ: toàn bộ cơ sở dữ liệu nằm trong một file. Nếu ngại dòng lệnh, DB Browser for SQLite cho phép dán cùng đoạn script vào tab “Execute SQL”.
- Cài SQLite: macOS và nhiều bản Linux đã có sẵn lệnh sqlite3; trên Windows, tải bộ công cụ dòng lệnh (sqlite-tools) từ trang chủ SQLite rồi giải nén.
- Tạo file cơ sở dữ liệu: mở terminal và chạy
sqlite3 cafe.db. File cafe.db được tạo ở thư mục hiện tại. - Dán script tạo bảng và dữ liệu bên dưới (khớp đúng hai bảng ở trên).
- Bật hiển thị dạng bảng: gõ
.headers onrồi.mode column. - Chạy từng câu SELECT trong bài, so kết quả với phần “Kết quả” ghi bên dưới mỗi câu. Thoát bằng
.quit.
CREATE TABLE cua_hang (
ma_ch INTEGER PRIMARY KEY,
ten TEXT NOT NULL,
thanh_pho TEXT NOT NULL
);
CREATE TABLE don_hang (
ma_don INTEGER PRIMARY KEY,
ma_ch INTEGER NOT NULL REFERENCES cua_hang(ma_ch),
ngay TEXT NOT NULL,
tong_tien INTEGER NOT NULL
);
INSERT INTO cua_hang VALUES
(1, 'Quận 1', 'Hồ Chí Minh'),
(2, 'Hoàn Kiếm', 'Hà Nội'),
(3, 'Hải Châu', 'Đà Nẵng'),
(4, 'Thủ Đức', 'Hồ Chí Minh');
INSERT INTO don_hang VALUES
(101, 1, '2026-09-01', 120000),
(102, 1, '2026-09-01', 85000),
(103, 2, '2026-09-02', 150000),
(104, 3, '2026-09-02', 60000),
(105, 2, '2026-09-03', 95000),
(106, 1, '2026-09-03', 70000);
Hai chi tiết cần biết khi chạy trên SQLite: nó không có kiểu ngày riêng nên cột ngày lưu dạng chuỗi theo định dạng năm-tháng-ngày (so sánh và sắp xếp vẫn đúng); và bạn nên gõ dấu chấm phẩy cuối mỗi câu, vì thiếu dấu này dòng lệnh sẽ chờ bạn gõ tiếp thay vì chạy.
SELECT, WHERE, ORDER BY: lấy, lọc, sắp xếp
SELECT ma_don, ngay, tong_tien
FROM don_hang
WHERE tong_tien >= 90000
ORDER BY tong_tien DESC;
Kết quả: đơn 103 (150.000), 101 (120.000), 105 (95.000). Hãy ghi rõ tên cột thay vì SELECT *: dễ đọc hơn, truyền ít dữ liệu hơn và không “vỡ” khi bảng thêm cột. WHERE còn hỗ trợ BETWEEN, IN, LIKE ‘Hà%’, IS NULL, kết hợp bằng AND, OR, NOT.
JOIN: nối hai bảng
Câu hỏi: “Mỗi cửa hàng có bao nhiêu đơn, doanh thu bao nhiêu?”
SELECT c.ten,
COUNT(d.ma_don) AS so_don,
COALESCE(SUM(d.tong_tien), 0) AS doanh_thu
FROM cua_hang c
LEFT JOIN don_hang d ON d.ma_ch = c.ma_ch
GROUP BY c.ten
ORDER BY doanh_thu DESC;
| ten | so_don | doanh_thu |
|---|---|---|
| Quận 1 | 3 | 275000 |
| Hoàn Kiếm | 2 | 245000 |
| Hải Châu | 1 | 60000 |
| Thủ Đức | 0 | 0 |
Đổi LEFT JOIN thành INNER JOIN, dòng Thủ Đức biến mất vì INNER JOIN chỉ giữ dòng khớp ở cả hai bảng. Đây là lỗi kinh điển khiến báo cáo “mất” các cửa hàng, sản phẩm chưa phát sinh giao dịch.
Cách đọc một câu JOIN cho dễ: FROM cua_hang c đặt bảng cua_hang làm “bảng trái” và đặt bí danh c; LEFT JOIN don_hang d là bảng phải; mệnh đề ON d.ma_ch = c.ma_ch nói hai dòng được ghép khi giá trị khoá bằng nhau. Khoá ngoại ma_ch ở don_hang chính là chỗ “trỏ” về khoá chính ma_ch của cua_hang.
| Kiểu JOIN | Giữ lại | Dùng khi |
|---|---|---|
| INNER JOIN | Dòng khớp ở cả hai bảng | Cần đủ dữ liệu hai phía |
| LEFT JOIN | Mọi dòng bảng trái, phía phải không khớp thì NULL | Báo cáo cần đủ danh sách gốc, kể cả mục bằng 0 |
| FULL OUTER JOIN | Mọi dòng của cả hai bảng | Đối soát hai nguồn xem bên nào thiếu (MySQL chưa hỗ trợ trực tiếp) |
| CROSS JOIN | Mọi tổ hợp dòng | Tạo khung, ví dụ mọi cửa hàng nhân mọi ngày trong tháng |
Bài tập SELECT và JOIN có đáp án
Hãy tự viết câu lệnh trên bảng mẫu trước, rồi mới mở đáp án. Mỗi bài luyện một điểm khác nhau.
- Bài 1 (WHERE): liệt kê mã đơn và tổng tiền của các đơn ngày 2026-09-02.
SELECT ma_don, tong_tien FROM don_hang WHERE ngay = '2026-09-02'; -- Kết quả: 103 (150000), 104 (60000) - Bài 2 (INNER JOIN): hiển thị mỗi đơn kèm tên cửa hàng.
SELECT d.ma_don, c.ten, d.tong_tien FROM don_hang d INNER JOIN cua_hang c ON c.ma_ch = d.ma_ch ORDER BY d.ma_don; -- Kết quả: 6 dòng; đơn 101, 102, 106 thuộc Quận 1 - Bài 3 (LEFT JOIN và IS NULL): tìm cửa hàng chưa có đơn nào.
SELECT c.ten FROM cua_hang c LEFT JOIN don_hang d ON d.ma_ch = c.ma_ch WHERE d.ma_don IS NULL; -- Kết quả: Thủ ĐứcDòng nào không khớp được bảng phải điền NULL ở mọi cột của bảng phải, nên lọc IS NULL trên khoá của bảng phải sẽ ra đúng những dòng “mồ côi”.
- Bài 4 (GROUP BY, AVG): giá trị trung bình mỗi đơn theo cửa hàng.
SELECT c.ten, AVG(d.tong_tien) AS trung_binh FROM cua_hang c JOIN don_hang d ON d.ma_ch = c.ma_ch GROUP BY c.ten; -- Kết quả: Quận 1 ≈ 91666,67; Hoàn Kiếm 122500; Hải Châu 60000Thủ Đức không xuất hiện vì dùng JOIN thường. Bạn có thể giải thích vì sao không?
- Bài 5 (điều kiện trong ON): doanh thu từng cửa hàng chỉ trong ngày 2026-09-01, cửa hàng không bán vẫn hiện số 0.
SELECT c.ten, COALESCE(SUM(d.tong_tien), 0) AS doanh_thu FROM cua_hang c LEFT JOIN don_hang d ON d.ma_ch = c.ma_ch AND d.ngay = '2026-09-01' GROUP BY c.ten; -- Kết quả: Quận 1 = 205000; ba cửa hàng còn lại = 0
Bài 5 là chỗ người mới hay vấp. Nếu chuyển điều kiện ngày xuống WHERE, các dòng NULL của cửa hàng không có đơn ngày 01/09 bị loại ngay, LEFT JOIN thành INNER JOIN và chỉ còn Quận 1. Quy tắc: điều kiện lọc bảng phía phải của LEFT JOIN đặt trong ON; điều kiện lọc bảng gốc đặt trong WHERE.
Lỗi thường gặp khi mới học SELECT và JOIN
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Báo lỗi cột không nằm trong GROUP BY (PostgreSQL, SQL Server) | SELECT có cột không gom nhóm và không bọc hàm tổng hợp | Thêm cột vào GROUP BY hoặc dùng MIN, MAX, SUM. Một số cấu hình MySQL cũ không báo lỗi nhưng trả giá trị tuỳ ý |
| Báo lỗi tên cột mơ hồ (ambiguous column) | Cả hai bảng đều có cột ma_ch mà không ghi bí danh | Viết d.ma_ch hoặc c.ma_ch |
| Tổng tiền lớn bất thường sau JOIN | Khoá nối không duy nhất nên dòng bị nhân lên | Đếm dòng trước và sau JOIN; tổng hợp trước rồi mới nối |
| Mất dòng sau khi thêm WHERE vào LEFT JOIN | WHERE trên cột bảng phải loại các dòng NULL | Chuyển điều kiện sang mệnh đề ON (bài 5) |
| WHERE = NULL không ra dòng nào | So sánh với NULL luôn không đúng | Dùng IS NULL hoặc IS NOT NULL |
| Dùng bí danh cột trong WHERE bị báo lỗi | WHERE chạy trước SELECT | Lặp lại biểu thức hoặc bọc bằng CTE |
GROUP BY và HAVING: tổng hợp theo nhóm
GROUP BY gom các dòng cùng giá trị thành nhóm, rồi áp hàm tổng hợp (COUNT, SUM, AVG, MIN, MAX). HAVING lọc trên kết quả đã tổng hợp. Câu hỏi: “Thành phố nào có doanh thu trên 100.000 đồng?”
SELECT c.thanh_pho, SUM(d.tong_tien) AS doanh_thu
FROM don_hang d
JOIN cua_hang c ON c.ma_ch = d.ma_ch
GROUP BY c.thanh_pho
HAVING SUM(d.tong_tien) > 100000;
Kết quả: Hồ Chí Minh 275.000 và Hà Nội 245.000; Đà Nẵng bị loại. Quy tắc dễ nhớ: WHERE lọc từng dòng trước khi gom, HAVING lọc từng nhóm sau khi gom.
Thứ tự viết khác thứ tự xử lý
Tài liệu của Microsoft về câu lệnh SELECT mô tả thứ tự xử lý logic: FROM, ON, JOIN, WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY rồi tới TOP. Các hệ khác theo cùng logic, với LIMIT ở cuối thay cho TOP.
Thứ tự này giải thích vì sao WHERE không lọc được bằng SUM (lúc WHERE chạy, nhóm chưa tồn tại), và vì sao bí danh đặt ở SELECT thường không dùng được ở WHERE nhưng dùng được ở ORDER BY. Thứ tự vật lý thực tế do bộ tối ưu quyết định.
UPDATE, DELETE: sửa dữ liệu an toàn
BEGIN;
SELECT COUNT(*) FROM don_hang WHERE ma_don = 106; -- kiểm tra trước
UPDATE don_hang SET tong_tien = 75000 WHERE ma_don = 106;
COMMIT; -- sai số dòng thì ROLLBACK
UPDATE và DELETE thiếu WHERE sẽ tác động lên toàn bộ bảng. Hãy đếm số dòng bằng SELECT cùng điều kiện trước, bọc thay đổi trong giao dịch và không dùng tài khoản có quyền ghi khi chỉ cần đọc.
CTE và hàm cửa sổ: truy vấn nhiều bước
CTE (Common Table Expression) là mệnh đề WITH, đặt tên cho kết quả trung gian để dùng như một bảng. Hàm cửa sổ, có trong chuẩn từ SQL:2003, tính toán trên nhóm dòng liên quan nhưng vẫn giữ nguyên từng dòng. Câu hỏi: “Đơn lớn nhất của mỗi cửa hàng là đơn nào?”
WITH xep_hang AS (
SELECT ma_ch, ma_don, tong_tien,
ROW_NUMBER() OVER (PARTITION BY ma_ch
ORDER BY tong_tien DESC) AS hang
FROM don_hang
)
SELECT ma_ch, ma_don, tong_tien
FROM xep_hang
WHERE hang = 1;
Kết quả: cửa hàng 1 là đơn 101, cửa hàng 2 là đơn 103, cửa hàng 3 là đơn 104. Các hàm cửa sổ hay dùng khác: RANK, DENSE_RANK (xếp hạng có đồng hạng), LAG, LEAD (so với kỳ trước, kỳ sau), SUM … OVER (luỹ kế).
NULL trong SQL: vì sao kết quả “thiếu dòng”?
NULL nghĩa là “không biết” hoặc “không có giá trị”, không phải số 0 hay chuỗi rỗng. Nhiều lỗi số liệu khó tìm đến từ đây:
- So sánh với NULL bằng dấu bằng không bao giờ đúng: phải viết IS NULL thay cho = NULL.
- COUNT(*) khác COUNT(cột): COUNT(cột) bỏ qua NULL. Ở ví dụ LEFT JOIN, COUNT(d.ma_don) cho Thủ Đức bằng 0, còn COUNT(*) cho 1.
- SUM, AVG bỏ qua NULL: SUM toàn NULL ra NULL, nên ví dụ trên dùng COALESCE để đổi thành 0.
- NOT IN gặp NULL: nếu truy vấn con trong NOT IN có một NULL, điều kiện có thể không trả về dòng nào. NOT EXISTS an toàn hơn.
Phương ngữ SQL: MySQL, PostgreSQL, SQL Server, Oracle khác nhau ở đâu?
Các hệ đều theo chuẩn ở phần lõi nhưng thêm cú pháp, hàm riêng và bộ mở rộng thủ tục riêng: Transact-SQL (T-SQL) của SQL Server, PL/SQL của Oracle, PL/pgSQL của PostgreSQL. Những khác biệt bạn sẽ gặp ngay tuần đầu:
| Việc cần làm | MySQL | PostgreSQL | SQL Server | Oracle | SQLite |
|---|---|---|---|---|---|
| Lấy 10 dòng đầu | LIMIT 10 | LIMIT 10 hoặc FETCH FIRST 10 ROWS ONLY | TOP (10) hoặc OFFSET … FETCH NEXT | FETCH FIRST 10 ROWS ONLY (bản mới), ROWNUM (bản cũ) | LIMIT 10 |
| Nối chuỗi | CONCAT(a, b) | a || b | a + b hoặc CONCAT | a || b | a || b |
| Thời điểm hiện tại | NOW() | NOW() | GETDATE() | SYSDATE | CURRENT_TIMESTAMP |
| Cột tự tăng | AUTO_INCREMENT | GENERATED AS IDENTITY, SERIAL | IDENTITY(1,1) | GENERATED AS IDENTITY, SEQUENCE | INTEGER PRIMARY KEY |
Lời khuyên: học SQL chuẩn trước, tra phần khác biệt khi cần. Truy vấn phải chạy trên nhiều hệ thì ưu tiên cú pháp chuẩn như FETCH FIRST, COALESCE, CURRENT_TIMESTAMP.
SQL và NoSQL khác nhau thế nào?
NoSQL là tên chung cho các cơ sở dữ liệu không lấy bảng quan hệ làm trung tâm: tài liệu, khoá – giá trị, cột rộng, đồ thị. Cơ sở dữ liệu SQL có lược đồ khai báo trước và giao dịch ACID, hợp với kế toán, đơn hàng, kho, báo cáo; NoSQL linh hoạt cấu trúc, hợp với dữ liệu thay đổi thường xuyên hoặc lượng ghi rất lớn. Ranh giới ngày càng mờ: nhiều hệ quan hệ hỗ trợ JSON, và chuẩn SQL:2023 đã có kiểu JSON lẫn truy vấn đồ thị. So sánh đầy đủ và cách chọn có trong bài so sánh SQL và NoSQL.
SQL cho người làm phân tích dữ liệu cần gì?
Với chuyên viên phân tích dữ liệu, SQL là công cụ lấy và chuẩn bị dữ liệu trước khi vẽ biểu đồ. Kỹ năng nên có theo thứ tự ưu tiên:
- Lọc, nối, gom nhóm thành thạo: biết khi nào LEFT, khi nào INNER JOIN; dùng CASE WHEN để phân loại.
- Xử lý ngày tháng: cắt theo ngày, tuần, tháng, so cùng kỳ. Hàm khác nhau theo hệ (DATE_TRUNC ở PostgreSQL, DATE_FORMAT ở MySQL).
- CTE và hàm cửa sổ: tỷ lệ giữ chân, xếp hạng, doanh thu luỹ kế, tăng trưởng so tháng trước.
- Đọc mô hình dữ liệu: hiểu bảng sự kiện (fact) và bảng chiều (dimension) trong kho dữ liệu.
Nguyên tắc để số liệu đáng tin:
- Cẩn thận JOIN gây nhân dòng: nối đơn hàng với chi tiết đơn rồi SUM tổng tiền đơn sẽ cộng trùng. Tổng hợp đúng cấp chi tiết trước rồi mới nối; đếm dòng trước và sau mỗi JOIN.
- Không chạy truy vấn nặng trên cơ sở dữ liệu vận hành: có thể làm chậm phần mềm bán hàng. Hãy truy vấn bản sao chỉ đọc hoặc kho dữ liệu đã qua quy trình ETL.
- Để ý chi phí đám mây: nhiều kho dữ liệu tính phí theo lượng dữ liệu quét. Chọn đúng cột, lọc theo cột phân vùng (thường là ngày).
- Thống nhất định nghĩa chỉ số: “doanh thu” đã trừ hoàn trả chưa? Ghi chú trong truy vấn và lưu vào kho mã chung. Một chỉ số sai ở câu SQL sẽ sai ở mọi biểu đồ business intelligence dùng nó.
Các mô hình ngôn ngữ lớn nay viết SQL từ câu hỏi tiếng Việt khá tốt (text-to-SQL), hữu ích để viết nháp hoặc chuyển phương ngữ. Nhưng AI có thể đoán sai tên cột, sai kiểu JOIN mà câu lệnh vẫn chạy và ra con số trông hợp lý. Bạn vẫn cần hiểu SQL để kiểm tra; khi cho AI kết nối dữ liệu, hãy dùng tài khoản chỉ đọc và không đưa dữ liệu cá nhân khách hàng vào công cụ chưa được phê duyệt.
SQL injection là gì và phòng thủ thế nào?
SQL injection (chèn mã SQL) là lỗ hổng xảy ra khi ứng dụng ghép thẳng dữ liệu người dùng nhập vào câu lệnh SQL, khiến cơ sở dữ liệu hiểu một phần dữ liệu là lệnh. Hậu quả có thể là lộ, sửa hoặc xoá dữ liệu. Đây là lỗi cách lập trình, không phải lỗi của ngôn ngữ SQL.
Lỗ hổng đã được biết hơn hai thập kỷ nhưng vẫn phổ biến: trong CWE Top 25 năm 2025 của MITRE, SQL injection (CWE-89) đứng thứ 2, sau Cross-site Scripting; trong OWASP Top 10:2025, nhóm Injection ở vị trí A05. Gốc rễ là kiểu ghép chuỗi sau (cách viết sai, nêu để nhận diện khi rà soát mã):
# SAI: dữ liệu nhập bị ghép thẳng vào câu lệnh
sql = "SELECT ma_don FROM don_hang WHERE ma_khach = '" + ma_khach + "'"
cursor.execute(sql)
# ĐÚNG: truy vấn tham số hoá, câu lệnh và dữ liệu gửi tách riêng
sql = "SELECT ma_don FROM don_hang WHERE ma_khach = %s"
cursor.execute(sql, (ma_khach,))
Tài liệu SQL Injection Prevention Cheat Sheet của OWASP nêu các lớp phòng thủ:
| Biện pháp | Lưu ý |
|---|---|
| Truy vấn tham số hoá (prepared statement) | Biện pháp chính, nên là mặc định cho mọi truy vấn có dữ liệu từ bên ngoài |
| Stored procedure viết đúng cách | Chỉ an toàn khi bên trong không tự ghép chuỗi thành SQL động |
| Kiểm tra đầu vào theo danh sách cho phép (allow-list) | Bắt buộc cho phần không tham số hoá được, như tên cột trong ORDER BY |
| Tự “thoát” ký tự đặc biệt (escaping) | OWASP xếp vào mức “rất không khuyến khích” vì dễ sót |
| Đặc quyền tối thiểu (least privilege) | Mỗi ứng dụng một tài khoản, không chạy bằng tài khoản quản trị; dùng view để giới hạn cột, dòng |
Nên bổ sung: dùng ORM hoặc thư viện có tham số mặc định, rà soát mã và quét lỗ hổng định kỳ trên môi trường thử nghiệm, không hiển thị lỗi chi tiết của cơ sở dữ liệu cho người dùng, ghi nhật ký truy vấn bất thường. Tường lửa ứng dụng web (WAF) chỉ là lớp phụ, không thay được việc sửa mã. Bức tranh rộng hơn có trong bài an ninh mạng là gì.
Lưu ý pháp lý: cơ sở dữ liệu thường chứa dữ liệu cá nhân của khách hàng và nhân viên. Luật Bảo vệ dữ liệu cá nhân số 91/2025/QH15 có hiệu lực từ 01/01/2026, đặt nghĩa vụ bảo vệ dữ liệu và mức phạt đáng kể với tổ chức vi phạm. Phân quyền SQL tối thiểu, che dữ liệu cá nhân khi phân tích và ghi nhật ký truy cập là những việc kỹ thuật cụ thể giúp đáp ứng yêu cầu này. Nội dung mang tính kiến thức, không thay thế tư vấn pháp lý.
Học SQL bắt đầu từ đâu?
SQL có “điểm vào” thấp: cú pháp gần tiếng Anh, học miễn phí trên máy cá nhân với SQLite hoặc PostgreSQL và thấy kết quả ngay. Phần thực hành SQLite và năm bài tập ở trên có thể dùng làm tuần 1 và tuần 3. Lộ trình gợi ý (ví dụ minh hoạ, điều chỉnh theo thời gian của bạn):
| Giai đoạn | Nội dung | Bài tập tự kiểm tra |
|---|---|---|
| Tuần 1 | SELECT, WHERE, ORDER BY, LIMIT; kiểu dữ liệu | 10 đơn lớn nhất trong tháng |
| Tuần 2 | Hàm tổng hợp, GROUP BY, HAVING, CASE WHEN, NULL | Doanh thu theo cửa hàng, phân loại đơn |
| Tuần 3 | Các loại JOIN; khoá chính, khoá ngoại; INSERT, UPDATE trong giao dịch | Danh sách cửa hàng kể cả chưa có đơn |
| Tuần 4 | Truy vấn con, CTE, hàm cửa sổ | Đơn lớn nhất mỗi cửa hàng, doanh thu luỹ kế |
| Tuần 5–6 | Chỉ mục, EXPLAIN, thiết kế bảng, phân quyền, truy vấn tham số hoá | Thiết kế cơ sở dữ liệu nhỏ cho một bài toán thật |
Ba mẹo: luyện trên dữ liệu bạn quan tâm thật sự, viết câu hỏi bằng tiếng Việt trước rồi mới dịch sang SQL, và luôn đối chiếu kết quả bằng một cách tính khác.
Câu hỏi thường gặp
SQL có phải là ngôn ngữ lập trình không?
SQL là ngôn ngữ truy vấn khai báo, chuyên cho dữ liệu, khác ngôn ngữ đa dụng như Python. Tuy vậy, chuẩn SQL từ SQL:1999 đã có truy vấn đệ quy và câu lệnh thủ tục, còn T-SQL, PL/SQL có biến, vòng lặp, xử lý lỗi, nên ranh giới không tuyệt đối.
Học SQL có khó không, mất bao lâu?
Phần cơ bản không khó; luyện đều vài tuần là tự viết được truy vấn báo cáo đơn giản. Phần cần thời gian hơn là tư duy theo tập hợp, xử lý NULL, tối ưu hiệu năng và thiết kế bảng.
Nên học MySQL, PostgreSQL hay SQL Server trước?
Chọn hệ mà công ty hoặc công việc bạn nhắm tới đang dùng. Chưa có định hướng thì PostgreSQL hoặc SQLite là lựa chọn miễn phí, bám sát chuẩn. Vững SQL chuẩn rồi, chuyển hệ chủ yếu là học phần khác biệt.
Học SQL cơ bản cần nắm những lệnh nào trước?
Bắt đầu với SELECT, WHERE, ORDER BY, rồi hàm tổng hợp kèm GROUP BY, sau đó INNER JOIN và LEFT JOIN. Năm lệnh này đủ để làm các bài tập trong bài. INSERT, UPDATE, DELETE học sau, vì chúng thay đổi dữ liệu và cần quen với giao dịch.
Người không làm IT có cần học SQL không?
Người làm kế toán, bán hàng, vận hành, marketing học SQL để tự lấy số liệu từ phần mềm nghiệp vụ hoặc kho dữ liệu thay vì chờ IT xuất file. Báo cáo nhanh hơn và ít sai do sao chép qua nhiều file Excel.
Làm sao biết website có lỗi SQL injection?
Rà soát mã nguồn tìm chỗ ghép chuỗi tạo câu lệnh SQL, quét lỗ hổng trên môi trường thử nghiệm và thuê đơn vị kiểm thử bảo mật có hợp đồng, phạm vi rõ ràng. Không tự kiểm thử hệ thống không thuộc quyền quản lý của bạn, vì có thể vi phạm pháp luật.
VBPO Tech Blog cập nhật tin tức và kiến thức công nghệ cho doanh nghiệp Việt Nam. Xem thêm các bài cùng chuyên mục Dữ liệu & phân tích.