SydexaSydexa

SQL Cơ Bản

Thế giới dữ liệu
SELECT cơ bảnLọc với WHERE
Hàm tổng hợpGROUP BY & HAVING
INNER JOINLEFT/RIGHT/FULL JOIN & Subquery
Hàm xử lý dữ liệu
UNION & CTESắp ra mắt
Window FunctionsSắp ra mắt
CRUD & CREATE TABLESắp ra mắt
Thiết kế CSDLSắp ra mắt
VIEW & ALTER TABLESắp ra mắt
Dự án cuối khóaSắp ra mắt
Trang chủKhoá họcSQL Cơ Bản
SELECT cơ bản
Cú pháp SELECT/FROM, chọn cột cụ thể vs '*', alias với AS, loại trùng bằng DISTINCT, sắp xếp với ORDER BY và giới hạn kết quả bằng LIMIT.

Lấy dữ liệu đầu tiên

Ở bài trước, bạn đã có tấm bản đồ của cơ sở dữ liệu: bảng, hàng, cột, khóa chính, khóa ngoại. Nhưng bản đồ để làm gì nếu cứ treo trên tường? Hôm nay bạn sẽ nói câu đầu tiên với database, và nhận về đúng dữ liệu mình muốn.

Nhớ lại câu chuyện: database là một thư viện khổng lồ có ông thủ thư (DBMS) chứ. Bạn không tự bới kệ. Thay vào đó, bạn đưa cho thủ thư một tờ phiếu yêu cầu: “Cho tôi xin tên và ngày xuất bản của các cuốn sách”. Tờ phiếu ấy trong SQL có tên là SELECT.

Tin vui: SELECT là câu lệnh bạn sẽ gõ nhiều nhất trong quá trình làm việc với dữ liệu và nó dễ đến bất ngờ. Chỉ trong bài này, bạn đã có thể trả lời những câu như “ba cuốn sách mới nhất là cuốn nào?” chỉ bằng một dòng.

Khởi động

1.Bạn cần 'tên và năm xuất bản của 3 cuốn sách mới nhất' trong một thư viện có hàng vạn cuốn. Theo đúng tinh thần làm việc với database, việc của bạn là gì?
1/1
1/1
Bài này bắt đầu gõ code thật:

Khác với bài trước, từ đây bạn sẽ thực hành trực tiếp trên SQL playground ngay trong trang. Cứ mạnh dạn gõ và chạy thử, sai thì chạy lại, không hỏng gì cả.

Bảng ta sẽ dùng suốt bài

Vẫn là bảng sinh_vien quen thuộc từ bài trước, lần này có thêm cột diem_tb (điểm trung bình) để ta có cái mà sắp xếp. Tổng cộng 6 sinh viên thuộc 3 lớp, dưới đây là 3 dòng đầu:

ma_svho_tenngay_sinhgioi_tinhlopdiem_tb
SV001Nguyễn Văn An2005-03-12NamCNTT018.5
SV002Trần Thị Bình2005-07-25NữCNTT017.0
SV003Lê Hoàng Cường2004-11-02NamCNTT029.2

Lộ trình bài học

Đây là con đường ta sẽ đi trong bài này:

Bản đồ hành trình

Bắt đầuSELECT & FROMLấy cột nào, từ bảng nào;đổi tên cột hiển thị bằng AS.DISTINCT · ORDER BY · LIMITBỏ trùng, sắp xếp (kể cả nhiều cột),giữ vài dòng đầu, công thức 'top N'.Thực hành10 bài chạy thật, trong đó 3 bàilà bẫy kinh điển của người mới.123Hoàn thành!

Câu truy vấn đầu tiên

Mọi câu lệnh lấy dữ liệu đều bắt đầu bằng đúng hai từ khóa: SELECT và FROM. Công thức tối giản đến mức có thể đọc thành một câu tiếng Việt:

SELECT những cột bạn muốn FROM bảng chứa chúng;

FROM trả lời câu hỏi “lấy từ bảng nào?”, còn SELECT trả lời “lấy những cột nào?”. Dấu chấm phẩy ; ở cuối đánh dấu kết thúc một câu lệnh.

Bạn có hai cách nói với database mình cần cột nào. Dấu *nghĩa là “cho tôi hết”, còn liệt kê tên cột nghĩa là “chỉ đúng mấy cột này thôi”. Cách thứ hai còn quyết định luôn thứ tự các cột hiển thị: bạn viết cột nào trước thì nó nằm bên trái, bất kể trong bảng nó nằm ở đâu.

Hai cách chọn cột
1-- Lấy tất cả các cột
2SELECT * FROM sinh_vien;
3
4-- Chỉ lấy đúng hai cột, và ta muốn lop đứng trước ho_ten
5SELECT lop, ho_ten FROM sinh_vien;

SELECT: chọn đúng cột cần lấy

SELECT … FROM sinh_vien;
ma_svho_tenngay_sinhlopSV001Nguyễn Văn An2005-03-12CNTT01SV002Trần Thị Bình2005-07-25CNTT01SV003Lê Hoàng Cường2004-11-02CNTT02
Bấm “Bắt đầu” để xem SELECT rọi “đèn pin” vào đúng những cột mình cần.
SELECT * tiện, nhưng đừng lạm dụng:

Khi viết ứng dụng thật, hãy ưu tiên liệt kê tên cột thay cho *. Câu lệnh sẽ nói rõ bạn cần gì, tránh vô tình kéo về những cột nặng (ảnh, mô tả dài) mà bạn không dùng đến.

AS - đặt tên hiển thị cho cột

Bạn vừa thấy bước cuối của animation trên: tiêu đề cột đổi từ ho_ten thành Họ và tên. Thủ phạm là từ khóa AS, thứ đặt cho cột một tên hiển thị (alias) trong bảng kết quả.

Tên cột trong database thường viết không dấu, dính liền để dễ gõ, nhưng khi xuất báo cáo cho người khác đọc thì diem_tb trông khô khan hơn hẳn “Điểm trung bình”. Nếu tên mới có dấu cách hoặc tiếng Việt, hãy bọc nó trong nháy kép.

Đặt tên hiển thị bằng AS
1SELECT
2 ho_ten AS "Họ và tên",
3 lop AS "Lớp",
4 diem_tb AS "Điểm trung bình"
5FROM sinh_vien;
AS chỉ là cái nhãn dán:

Điều quan trọng nhất cần nhớ: AS chỉ đổi nhãn ở kết quả trả về. Cột trong bảng vẫn tên ho_ten, dữ liệu không thay đổi một ký tự. Chạy lại câu SELECT * FROM sinh_vien là bạn thấy mọi thứ y như cũ.

Vậy là bạn đã lấy được cột mình cần. Nhưng thử hỏi database một câu rất đời thường xem: “trường đang có những lớp nào?”. Viết SELECT lop FROM sinh_vien thì kết quả trả về sáu dòng, trong đó CNTT01 lặp lại tận ba lần, còn thứ tự thì lộn xộn. Rõ ràng SELECT và FROM vẫn còn thiếu vài người bạn.

Kiểm tra nhanh

1.Câu lệnh SELECT ho_ten, lop FROM sinh_vien; trả về điều gì?
1/2
1/2

DISTINCT - mỗi giá trị chỉ một lần

Người bạn thứ nhất giải quyết chuyện lặp lại. Sáu dòng lop vừa rồi bị trùng là chuyện đương nhiên, vì bảng lưu mỗi sinh viên một dòng chứ đâu lưu mỗi lớp một dòng. Nhưng câu hỏi của bạn không phải “có bao nhiêu sinh viên”, mà là có bao nhiêu giá trị khác nhau.

DISTINCT gộp các dòng trùng nhau lại, giữ mỗi giá trị đúng một lần.

SELECT lop FROM sinh_vien;
Cột lop (5 dòng)DISTINCT lop (2 lớp)lopCNTT01CNTT01CNTT02CNTT02CNTT01CNTT01gộp từ 3 dòngCNTT02gộp từ 2 dòng
Bấm “Bắt đầu”: cột lop có 5 dòng, nhưng thật ra bao nhiêu lớp khác nhau?
Danh sách lớp không trùng
1SELECT DISTINCT lop FROM sinh_vien;
DISTINCT áp lên cả bộ cột, không riêng cột đầu:

Đây là hiểu lầm phổ biến nhất về DISTINCT. Nó không đứng riêng với cột ngay sau nó, mà xét toàn bộ tổ hợp các cột bạn liệt kê: hai dòng chỉ bị coi là trùng khi giống nhau ở mọi cột. Với bộ dữ liệu của bài này, SELECT DISTINCT lop cho 3 dòng, nhưng thêm một cột nữa thành SELECT DISTINCT lop, gioi_tinhthì ra tới 5 dòng, vì giờ mỗi cặp lớp-giới tính mới là một “giá trị”. Bạn sẽ tự tay chạy thử ở phần thực hành.

ORDER BY - sắp xếp kết quả

Một điều nhiều người mới hay bất ngờ: mặc định, thứ tự các dòng mà database trả về là không đảm bảo. Muốn có thứ tự chắc chắn, bạn phải nói ra bằng ORDER BY.

Thêm ASC để sắp tăng dần (đây cũng là mặc định nếu bạn không ghi gì), hoặc DESC để sắp giảm dần.

Kiểu dữ liệuASC (tăng dần)DESC (giảm dần)
Số (điểm)0 → 10 (thấp đến cao)10 → 0 (cao đến thấp)
Chữ (họ tên)A → ZZ → A
Ngày (ngay_sinh)Cũ nhất → mới nhấtMới nhất → cũ nhất (trẻ nhất trước)

Bạn còn có thể đưa cho ORDER BY nhiều cột, cách nhau bằng dấu phẩy. Database sắp theo cột đầu tiên trước; chỉ khi nào hai dòng bằng nhau ở cột đó nó mới lấy cột thứ hai ra phân xử. Mỗi cột lại có chiều riêng của nó, nên chuyện sắp cột này tăng còn cột kia giảm là hoàn toàn bình thường.

Xếp theo lớp, trong mỗi lớp xếp điểm từ cao xuống thấp
1SELECT lop, ho_ten, diem_tb
2FROM sinh_vien
3ORDER BY lop ASC, diem_tb DESC;

Kết quả đọc rất đã mắt: ba bạn lớp CNTT01 nằm liền một khối và trong khối đó điểm giảm dần (8.5 → 8.0 → 7.0), rồi mới tới khối CNTT02, cuối cùng là CNTT03. Đây chính là dạng bảng xếp hạng theo nhóm mà bạn gặp suốt ngày ngoài đời.

LIMIT - chỉ lấy vài dòng đầu

Bảng thật có thể có hàng triệu dòng nhưng hiếm khi bạn muốn xem hết. Vậy nên ta dùng LIMIT n giữ lại đúng n dòng đầu của kết quả.

Ghép ORDER BY với LIMIT sẽ tạo ra công thức kinh điển để lấy “top N”: 3 bạn trẻ nhất, 5 điểm cao nhất, 10 đơn hàng mới nhất… Sắp xếp trước, rồi cắt lấy phần đầu.

ORDER BY + LIMIT: 3 bạn trẻ nhất

SELECT * FROM sinh_vien;
ma_svho_tenngay_sinh SV001Nguyễn Văn An2005-03-12SV002Trần Thị Bình2005-07-25SV003Lê Hoàng Cường2004-11-02SV004Phạm Thu Dung2006-01-30SV005Vũ Minh Đức2005-09-08✂ LIMIT 3
Bấm “Bắt đầu” để tìm 3 bạn trẻ nhất trong danh sách.
3 sinh viên trẻ nhất
1SELECT ho_ten, ngay_sinh
2FROM sinh_vien
3ORDER BY ngay_sinh DESC
4LIMIT 3;
LIMIT mà không có ORDER BY là một lời hứa suông:

LIMIT chỉ biết cắt lấy n dòng đầu của kết quả, nó hoàn toàn không biết “giỏi nhất” hay “mới nhất” nghĩa là gì. Viết SELECT ho_ten, diem_tb FROM sinh_vien LIMIT 3 là bạn nhận về ba dòng bất kỳ mà database thấy tiện lấy trước, không phải ba bạn điểm cao nhất. Muốn có nghĩa thì phải xếp hạng trước đã: ORDER BY làm việc chọn ai đứng đầu, LIMIT chỉ làm mỗi việc cầm kéo.

Thứ tự viết một câu truy vấn:

Các mảnh ghép luôn xuất hiện theo đúng thứ tự này: SELECT → FROM → ORDER BY → LIMIT. Viết sai thứ tự (ví dụ đặt LIMIT trước ORDER BY) là database sẽ báo lỗi ngay.

Kiểm tra nhanh

1.Bảng sinh_vien có 200 dòng thuộc 5 lớp. SELECT DISTINCT lop FROM sinh_vien; trả về bao nhiêu dòng?
1/3
1/3

Thực hành

Mười bài tăng dần độ khó trên bảng sinh_vien, chạy thật chứ không phải kết quả dựng sẵn. Đọc yêu cầu, gõ (hoặc chỉnh) câu lệnh rồi bấm chạy để xem kết quả. Ba câu bẫy nằm rải ở câu 4, 9 và 10: chúng cố tình cho ra kết quả sai hoặc báo lỗi, và việc của bạn là sửa lại cho đúng.

PostgreSQL 17 · Sandbox
Premium

Mở khoá SQL Sandbox: thực hành trên database thật

SQL Playground chỉ mở cho học viên đã mua khoá “SQL Cơ Bản”. Khi mua, bạn được chạy trực tiếp query trên PostgreSQL với dataset thật của bài học.

  • Chạy SQL trực tiếp trên PostgreSQL 17 thật, không phải kết quả mô phỏng
  • Mỗi học viên có schema riêng, tự do CREATE / INSERT / EXPLAIN
  • Reset dữ liệu về trạng thái gốc bất cứ lúc nào để thử lại
299.000₫
Nghịch thêm cho nhớ lâu:

Ở câu 8, đổi LIMIT 3 thành LIMIT 1 để tìm bạn trẻ nhất, hay đổi DESC thành ASC để tìm bạn lớn tuổi nhất. Ở câu 4, thử thêm tiếp cột ngay_sinh vào sau DISTINCT: mỗi sinh viên có một ngày sinh riêng nên chẳng còn cặp nào trùng, kết quả nở lại đủ 6 dòng. Càng thêm cột thì DISTINCT càng khó gộp. Sai thì chạy lại, không hỏng gì cả.

Tổng kết bài học

Chỉ với vài từ khóa, bạn đã có thể “trò chuyện” với database đủ kiểu. Đây là tấm cheat sheet bỏ túi để xem lại bất cứ lúc nào:

SELECTChọn CỘT muốn lấy ra
FROMChỉ ra BẢNG chứa dữ liệu
*Lấy hết mọi cột, tiện xem nhanh
tên cộtLiệt kê đúng cột cần: gọn, nhẹ, rõ
ASĐổi tên hiển thị của cột (alias)
DISTINCTLoại dòng trùng, xét trên CẢ BỘ cột liệt kê
ORDER BYSắp xếp tăng (ASC) hoặc giảm (DESC)
ORDER BY a, bBằng nhau ở cột a thì mới xét tới cột b
LIMITGiữ lại n dòng đầu tiên
Mẹo bỏ túi: mọi câu hỏi “top N”:

Ghép ORDER BY với LIMITlà bạn trả lời được vô số câu hỏi “top N”: 3 bạn trẻ nhất, 5 điểm cao nhất, 10 đơn mới nhất… Bí quyết luôn giống nhau: sắp xếp trước, rồi cắt lấy phần đầu.

Nói thì dễ, ghép mới thấm. Dưới đây là một câu duy nhất gom lại gần như mọi từ khóa bạn vừa học. Kéo từng mảnh vào chỗ trống, điền sai cũng không sao vì database sẽ trả lời bạn y như thật.

Ghép câu SQL tổng kết cả bài

ĐỀ BÀI

In bảng điểm dán bảng tin: gom các bạn cùng lớp thành một khối và để CNTT01 lên trước, trong mỗi lớp bạn điểm cao đứng trên, tiêu đề cột họ tên đổi thành Họ và tên, và chỉ in 4 dòng đầu cho vừa tờ giấy.

Đã điền 0/6 chỗ trống
SELECTlop, ho_ten, diem_tb
FROM
lop, diem_tb
LIMIT;

Kéo chip vào chỗ trống, hoặc bấm chip rồi bấm chỗ trống. Bấm vào chỗ đã điền để lấy chip ra.

Đặt tên cột
Tên bảng
Từ khoá
Chiều
Số dòng

Điền đủ sáu chỗ trống rồi database mới chạy được câu này. Mỗi chỗ chỉ nhận đúng loại của nó, nên cứ thử thoải mái - sai thì đọc câu ERROR cũng là học.

Có một điều đáng để ý: suốt cả bài, mọi câu lệnh của bạn đều trả về toàn bộ sinh viên trong bảng. SELECT chọn cột, ORDER BY đổi thứ tự, LIMITcắt bớt phần đuôi, nhưng chưa có công cụ nào giúp bạn nói “chỉ lấy những bạn thỏa điều kiện này”. Đó chính xác là chỗ trống mà WHERE của bài sau lấp vào.

Ôn nhanh bằng flashcard

Nhấp vào từng thẻ để lật và tự kiểm tra trước khi làm quiz.

SELECT … FROM … dùng để làm gì?

Nhấp vào thẻ để lật

Lấy dữ liệu ra khỏi database: SELECT nói lấy CỘT nào, FROM nói lấy từ BẢNG nào. Ví dụ: SELECT ho_ten FROM sinh_vien.

Nhấp vào thẻ để lật lại
1/8

Kiểm tra kiến thức cuối bài

1.Trong câu SELECT ho_ten FROM sinh_vien;, mỗi từ khóa đóng vai trò gì?
1/9
1/9
Bài trướcBài tiếp theo