SQL cho phân tích dữ liệu · Hướng dẫn · 30 tháng 10, 2026 · 13 phút đọc
SQL và Excel trong phân tích dữ liệu: mỗi công cụ mạnh ở đâu và cách chuyển sang SQL

Excel và SQL không hẳn là đối thủ, mà là công cụ cho những giai đoạn khác nhau của cùng một công việc. SQL mạnh hơn ở việc lấy, ghép và tổng hợp dữ liệu: nó xử lý hàng triệu dòng, đọc dữ liệu trực tiếp từ nơi dữ liệu được lưu, và mỗi truy vấn là bản ghi chép chính xác những gì bạn đã làm, có thể chạy lại vào ngày mai. Excel mạnh hơn ở việc xem, chỉnh và trình bày một kết quả nhỏ: mô hình what-if, biểu đồ nhanh, sửa tay, và một file mà ai trong công ty cũng mở được. Phần lớn data analyst dùng cả hai mỗi ngày: SQL để tạo ra một bảng vài trăm dòng, Excel hoặc công cụ BI để làm việc với bảng đó.
Nếu bạn đã biết Excel, bạn biết SQL nhiều hơn mình nghĩ. Pivot table là GROUP BY. VLOOKUP là JOIN. Filter là WHERE. Bài này chuyển những thói quen Excel bạn đang có sang SQL, với các truy vấn chạy trên cơ sở dữ liệu hãng hàng không của khóa SQL trên Nomto (104 sân bay, 33.121 chuyến bay, 262.788 đơn đặt chỗ, tiền tính bằng đô la Mỹ). Mọi bảng kết quả bên dưới đều là kết quả chạy thật.
Trả lời ngắn: khi nào dùng công cụ nào
| Việc cần làm | Tốt hơn với | Vì sao |
|---|---|---|
| Lấy dữ liệu từ cơ sở dữ liệu của công ty | SQL | dữ liệu nằm ở đó; xuất file là một bước thủ công và sẽ nhanh chóng lỗi thời |
| Hơn khoảng một triệu dòng | SQL | một sheet Excel chứa tối đa 1.048.576 dòng |
| Ghép nhiều bảng | SQL | một JOIN thay cho một cột tra cứu cho mỗi bảng |
| Báo cáo dựng lại hằng tuần | SQL | chạy lại truy vấn; không có bước copy-paste nào để quên |
| Kiểm tra một con số được tạo ra thế nào | SQL | truy vấn chính là công thức đầy đủ |
| Kịch bản what-if và mô hình tài chính | Excel | đổi một ô, thấy mọi con số phụ thuộc cập nhật theo |
| Xem nhanh một file nhỏ một lần | Excel | mở, sắp xếp, nhìn qua, xong |
| Sửa tay, ghi chú, tô màu | Excel | SQL không cho bạn gõ vào kết quả |
| Biểu đồ cho buổi họp trong năm phút | Excel | có sẵn và ai cũng quen |
| Chia sẻ với người không biết lập trình | Excel | ai cũng có và mở được |
Một quy tắc hữu ích: nếu việc cần làm là “tìm, ghép và đếm”, hãy bắt đầu bằng SQL. Nếu việc cần làm là “nghịch với bảng này và cho ai đó xem”, Excel là đủ, và thường còn tốt hơn.
Vì sao giới hạn số dòng quan trọng hơn bạn tưởng
Trong cơ sở dữ liệu của khóa học này, ticket_flights (mỗi hành khách trên mỗi chặng bay một dòng) có 1.045.726 dòng, vừa đủ nhét vào một sheet. Bảng sự kiện ứng dụng thì hoàn toàn không vừa:
SELECT COUNT(*) FROM app_events;
| count_star() |
|---|
| 1494723 |
Và vừa không có nghĩa là làm việc thoải mái: một workbook có một triệu dòng tra cứu và công thức sẽ tính lại rất chậm. Trong SQL, kích thước bảng gần như không thay đổi cách bạn viết truy vấn.
Filter → WHERE
Trong Excel, bạn bấm mũi tên filter và tích chọn giá trị. Trong SQL, bạn viết điều kiện. Các đơn đặt chỗ được tạo trong tháng 8 với giá trị từ $1.000 trở lên, và tổng của chúng:
SELECT COUNT(*) AS bookings, SUM(total_amount) AS revenue
FROM bookings
WHERE book_date >= '2026-08-01'
AND total_amount >= 1000;
| bookings | revenue |
|---|---|
| 16081 | 29341684.00 |
Các cách viết tương đương:
| Filter trong Excel | SQL |
|---|---|
| tích “Arrived” và “Departed” | WHERE status IN ('Arrived', 'Departed') |
| Number filter: lớn hơn hoặc bằng | WHERE total_amount >= 1000 |
| Text filter: bắt đầu bằng | WHERE city LIKE 'Novo%' |
| Date filter: trong khoảng | WHERE book_date >= '2026-08-01' AND book_date < '2026-09-01' |
| (Blanks) | WHERE actual_departure IS NULL |
Có một khác biệt hay làm người dùng Excel vấp: trong SQL, ô trống là NULL, và = NULL không bao giờ khớp với gì. Hãy dùng IS NULL.
Sắp xếp và Top 10 → ORDER BY và LIMIT
“Sắp xếp từ lớn đến nhỏ, xem năm dòng đầu” được viết là:
SELECT book_ref, book_date, total_amount
FROM bookings
ORDER BY total_amount DESC
LIMIT 5;
| book_ref | book_date | total_amount |
|---|---|---|
| 3B54BB | 2026-07-01 17:08:00 | 12045.00 |
| 3AC131 | 2026-07-27 01:06:00 | 10871.00 |
| 65A6EA | 2026-06-29 06:28:00 | 10656.00 |
| D7E9AA | 2026-08-04 05:29:00 | 10628.00 |
| EF479E | 2026-07-29 15:58:00 | 10351.00 |
Trong Excel, sắp xếp làm thay đổi sheet. Trong SQL, nó chỉ thay đổi kết quả này; bảng vẫn giữ nguyên như cũ.
VLOOKUP / XLOOKUP → JOIN
Trong flights, sân bay và máy bay chỉ xuất hiện dưới dạng mã. Trong Excel, bạn sẽ thêm ba cột tra cứu, mỗi cột trỏ sang một sheet khác. Trong SQL, đó là ba JOIN trong một truy vấn:
SELECT f.flight_no,
f.scheduled_departure,
dep.city AS from_city,
arr.city AS to_city,
a.model
FROM flights f
JOIN airports dep ON dep.airport_code = f.departure_airport
JOIN airports arr ON arr.airport_code = f.arrival_airport
JOIN aircrafts a ON a.aircraft_code = f.aircraft_code
WHERE f.departure_airport = 'SVO'
AND f.scheduled_departure >= '2026-08-01'
AND f.scheduled_departure < '2026-08-02'
ORDER BY f.scheduled_departure, f.flight_no
LIMIT 4;
| flight_no | scheduled_departure | from_city | to_city | model |
|---|---|---|---|---|
| PG0120 | 2026-08-01 09:00:00 | Moscow | Mirny | Boeing 737-300 |
| PG0591 | 2026-08-01 09:00:00 | Moscow | Tomsk | Sukhoi Superjet-100 |
| PG0241 | 2026-08-01 09:40:00 | Moscow | Cheboksary | Sukhoi Superjet-100 |
| PG0677 | 2026-08-01 09:40:00 | Moscow | Makhachkala | Boeing 737-300 |
Chú ý rằng cùng một bảng airports được JOIN hai lần, mỗi lần cho một đầu của chuyến bay, với hai bí danh khác nhau. Trong Excel, đó sẽ là hai lần tra cứu riêng trên cùng một sheet.
Có hai hành vi khác với VLOOKUP, và cả hai đều quan trọng:
Không có giá trị khớp. VLOOKUP hiện #N/A. JOIN thông thường (inner join) âm thầm bỏ dòng đó đi. Nếu muốn giữ dòng với giá trị trống, hãy dùng LEFT JOIN. Đây là mẫu máy bay duy nhất không có chuyến bay nào, thứ mà inner join sẽ che mất:
SELECT a.model
FROM aircrafts a
LEFT JOIN flights f ON f.aircraft_code = a.aircraft_code
WHERE f.flight_id IS NULL;
| model |
|---|
| Airbus A320-200 |
Nhiều giá trị khớp. VLOOKUP trả về giá trị khớp đầu tiên và bỏ qua phần còn lại. JOIN trả về mọi giá trị khớp, lặp lại dòng một lần cho mỗi giá trị. Một đơn đặt chỗ có thể có tới năm vé:
SELECT b.book_ref, b.total_amount, COUNT(t.ticket_no) AS tickets
FROM bookings b
JOIN tickets t ON t.book_ref = b.book_ref
GROUP BY b.book_ref, b.total_amount
ORDER BY tickets DESC, b.book_ref
LIMIT 3;
| book_ref | total_amount | tickets |
|---|---|---|
| 070133 | 1133.00 | 5 |
| 070682 | 8012.00 | 5 |
| 111C34 | 2120.00 | 5 |
Vì vậy nếu bạn JOIN đơn đặt chỗ với vé rồi cộng total_amount, mỗi đơn bị tính một lần cho mỗi vé:
SELECT (SELECT SUM(total_amount) FROM bookings) AS correct,
SUM(b.total_amount) AS after_join
FROM bookings b
JOIN tickets t ON t.book_ref = b.book_ref;
| correct | after_join |
|---|---|
| 207669809.00 | 343206192.00 |
Đây là lỗi SQL phổ biến nhất của người chuyển từ Excel sang, vì VLOOKUP không bao giờ nhân dòng. Trước khi JOIN, hãy tự hỏi: với một dòng bên trái, có thể có bao nhiêu dòng khớp bên phải? Bài JOIN trong SQL giải thích chi tiết chuyện này.
Pivot table → GROUP BY
Vùng Rows = các cột trong GROUP BY. Vùng Values = các hàm tổng hợp. Doanh thu theo hạng ghế, kèm tỷ trọng của từng hạng trên tổng (trong pivot là “Show values as % of grand total”):
SELECT fare_conditions,
COUNT(*) AS segments,
SUM(amount) AS revenue,
ROUND(100.0 * SUM(amount) / SUM(SUM(amount)) OVER (), 1) AS pct_of_revenue
FROM ticket_flights
GROUP BY fare_conditions
ORDER BY revenue DESC;
| fare_conditions | segments | revenue | pct_of_revenue |
|---|---|---|---|
| Economy | 920793 | 146956844.00 | 70.8 |
| Business | 107642 | 55051796.00 | 26.5 |
| Comfort | 17291 | 5661169.00 | 2.7 |
Truy vấn này chạy trên hơn một triệu dòng. SUM(SUM(amount)) OVER () là một hàm cửa sổ (window function) đặt tổng chung bên cạnh mỗi nhóm; bài hàm cửa sổ trong SQL giải thích cách hoạt động.
Một pivot có vùng Columns (trạng thái theo hàng dọc, tháng theo hàng ngang) là một phép đếm có điều kiện cho mỗi cột:
SELECT status,
COUNT(*) FILTER (WHERE EXTRACT(MONTH FROM scheduled_departure) = 7) AS jul,
COUNT(*) FILTER (WHERE EXTRACT(MONTH FROM scheduled_departure) = 8) AS aug,
COUNT(*) FILTER (WHERE EXTRACT(MONTH FROM scheduled_departure) = 9) AS sep,
COUNT(*) AS total
FROM flights
GROUP BY status
ORDER BY total DESC;
| status | jul | aug | sep | total |
|---|---|---|---|---|
| Arrived | 10821 | 5886 | 0 | 16707 |
| Scheduled | 0 | 10345 | 5038 | 15383 |
| On Time | 0 | 518 | 0 | 518 |
| Cancelled | 5 | 6 | 403 | 414 |
| Departed | 0 | 58 | 0 | 58 |
| Delayed | 0 | 41 | 0 | 41 |
Ở điểm này, thành thật mà nói Excel tiện hơn: pivot tự dựng các cột cho bạn, còn trong SQL bạn phải liệt kê chúng. Nhiều data analyst chia việc đúng như vậy: SQL tạo ra bảng dài (trạng thái, tháng, số đếm), còn pivot trong Excel hoặc công cụ BI trải nó ra thành các cột.
Vùng Filters của pivot là WHERE (trước khi nhóm), còn “chỉ hiện những nhóm có tổng trên N” là HAVING. Xem bài GROUP BY và HAVING.
Công thức → biểu thức
| Excel | SQL |
|---|---|
=A2*B2, =ROUND(A2, 2) |
price * qty, ROUND(amount, 2) trong SELECT |
=IF(A2>=1000, "large", "small") |
CASE WHEN total_amount >= 1000 THEN 'large' ELSE 'small' END |
=IFERROR(A2/B2, "") |
a / NULLIF(b, 0) |
=COUNTIF(range, "Cancelled") |
COUNT(*) FILTER (WHERE status = 'Cancelled') |
=SUMIFS(amount, class, "Business") |
SUM(amount) FILTER (WHERE fare_conditions = 'Business') |
=A2 & " " & B2 |
a || ' ' || b |
=LEFT(A2, 3), =UPPER(A2), =TRIM(A2) |
LEFT(s, 3), UPPER(s), TRIM(s) |
=EOMONTH, =YEAR, =A2-B2 với ngày |
DATE_TRUNC, EXTRACT(YEAR FROM d), d1 - d2 |
| Remove Duplicates | SELECT DISTINCT / COUNT(DISTINCT col) |
cột tổng lũy kế =C1+B2 |
SUM(x) OVER (ORDER BY day) |
Thay đổi tư duy lớn nhất: công thức Excel nằm trong một ô và bạn kéo nó xuống. Biểu thức SQL được viết một lần và áp dụng cho mọi dòng. Những hàm IF lồng nhau trở thành một CASE, đọc từ trên xuống dưới:
SELECT book_ref, total_amount,
CASE WHEN total_amount >= 1000 THEN 'large'
WHEN total_amount >= 300 THEN 'medium'
ELSE 'small'
END AS size
FROM bookings
ORDER BY book_ref
LIMIT 4;
| book_ref | total_amount | size |
|---|---|---|
| 00000F | 2657.00 | large |
| 000012 | 379.00 | medium |
| 000068 | 181.00 | small |
| 000181 | 1318.00 | large |
Còn cột mà bạn sẽ thêm bên cạnh rồi làm pivot theo nó chỉ là thêm một GROUP BY:
SELECT CASE WHEN total_amount >= 1000 THEN 'large'
WHEN total_amount >= 300 THEN 'medium'
ELSE 'small'
END AS size,
COUNT(*) AS bookings
FROM bookings
GROUP BY size
ORDER BY bookings DESC;
| size | bookings |
|---|---|
| medium | 130009 |
| small | 68305 |
| large | 64474 |
Remove Duplicates, ví dụ có bao nhiêu thành phố khác nhau có sân bay:
SELECT COUNT(*) AS airports, COUNT(DISTINCT city) AS cities
FROM airports;
| airports | cities |
|---|---|
| 104 | 101 |
Những chỗ Excel thực sự thắng
Cần nói thẳng điều này, vì “SQL tốt hơn” không đúng với mọi việc:
- Lập mô hình. Ngân sách, dự báo hay kịch bản giá, nơi bạn đổi một giả định và nhìn phần còn lại thay đổi theo, chính là thứ bảng tính được tạo ra để làm. SQL không có ô nào để đổi.
- Dữ liệu nhỏ, lộn xộn, dùng một lần. Một danh sách ai đó gửi qua email, có ô gộp và ghi chú bên lề, sửa tay nhanh hơn là nạp vào cơ sở dữ liệu.
- Nhập liệu thủ công. Thêm một nhận xét, sửa một giá trị, tô màu những dòng bạn muốn thảo luận.
- Giao tiếp. Sếp của bạn mở được một workbook. Phần lớn không mở được một truy vấn.
- Power Query và Data Model. Excel hiện đại có thể nạp và ghép dữ liệu lớn hơn sức chứa của một sheet. Nếu công ty bạn vận hành bằng Excel, các tính năng này thu hẹp một phần khoảng cách, dù logic vẫn khó rà soát hơn một truy vấn.
Đổi lại, SQL cho bạn khả năng tái lập. Một bảng tính đã qua hai mươi bước thủ công thì khó kiểm tra hay dựng lại; một truy vấn có thể đọc từng dòng, chạy lại trên dữ liệu tháng sau và được đồng nghiệp rà soát.
Cách chuyển sang SQL: một lộ trình thực tế
- Học cách hiểu một dòng. Trước mỗi truy vấn, hãy biết một dòng của mỗi bảng nghĩa là gì (một chuyến bay, một đơn đặt chỗ, một chặng bay trên vé). Điều này thay cho thói quen cuộn sheet.
- Bắt đầu từ những gì bạn đã làm trong Excel. Filter →
WHERE, sắp xếp →ORDER BY, pivot →GROUP BY. Làm lại một báo cáo hằng tuần thật của bạn bằng SQL. - Sau đó là JOIN. Thay các VLOOKUP, và kiểm tra số dòng trước và sau mỗi JOIN.
- Giữ Excel cho bước cuối. Xuất kết quả cuối cùng, nhỏ gọn, ra sheet để vẽ biểu đồ và ghi chú. Đó không phải gian lận; phần lớn data analyst làm việc như vậy.
- Thêm
CASE, CTE và hàm cửa sổ khi phần cơ bản đã thành quen tay. SQL cheat sheet có tất cả trên một trang.
Trong phòng học SQL của Nomto, bạn chạy các truy vấn này ngay trong trình duyệt trên cơ sở dữ liệu hãng hàng không, không cần cài gì, và khóa học bắt đầu đúng từ những thao tác giống Excel: chọn cột, lọc, sắp xếp và nhóm. Bài câu lệnh SQL cơ bản là bài nên đọc đầu tiên.
Câu hỏi thường gặp
SQL có khó học hơn Excel không?
Phần cơ bản thì không. Nếu bạn dựng được pivot table và dùng được VLOOKUP, bạn có thể học SELECT, WHERE, GROUP BY và JOIN trong vài tuần. Điều cần luyện tập là tư duy theo cả bảng thay vì từng ô, và nhận ra khi nào một JOIN làm nhân dòng.
SQL có thay thế hoàn toàn Excel được không?
Với việc lấy và tổng hợp dữ liệu thì phần lớn là có. Với lập mô hình, sửa tay và trình bày nhanh thì không. Phần lớn data analyst dùng SQL để tạo ra một kết quả nhỏ, rồi dùng bảng tính hoặc công cụ BI để làm việc với nó.
Data analyst có còn dùng Excel không?
Có, rất rộng rãi, nhất là ở bước cuối của phân tích và khi chia sẻ kết quả. SQL là thứ lấy dữ liệu; Excel, Google Sheets hay công cụ BI thường là thứ mọi người nhìn thấy.
Hàm tương đương VLOOKUP trong SQL là gì?
Một JOIN theo cột tra cứu: FROM flights f JOIN aircrafts a ON a.aircraft_code = f.aircraft_code. Hãy dùng LEFT JOIN nếu muốn giữ những dòng không có giá trị khớp, giống cách VLOOKUP giữ dòng và hiện #N/A.
Ngôn ngữ khác: English, Español, Português (Brasil), 简体中文