Tuần 1 · Ngày 4 · Bài mới · 4 giờ
Mục tiêu
- Ôn nhanh SUMIFS, COUNTIFS, AVERAGEIFS, XLOOKUP, PivotTable
- Ôn SELECT, WHERE, GROUP BY, JOIN, LEFT JOIN trên bộ dữ liệu Sao Mai
- Hiểu khái niệm bảng Fact/Dim, khóa, quan hệ, những thứ sẽ gặp lại trong Power BI
- Biết mỗi thao tác Excel/SQL tương ứng với tính năng nào trong Power BI
Dữ liệu cần tải
Ngày 4: Ôn Excel & SQL
Bạn đã biết Excel cơ bản và SQL SELECT / JOIN. Bài hôm nay không dạy lại từ đầu. Mục tiêu là ôn nhanh trên dữ liệu Sao Mai và thấy rõ: mọi thứ bạn làm bằng Excel/SQL đều có “anh em” trong Power BI.
Kế hoạch 4 giờ
| Thời gian | Nội dung |
|---|---|
| 1 giờ 15 | Phần 1: Excel |
| 1 giờ 45 | Phần 2 - 3: SQL, mô hình dữ liệu |
| 1 giờ | Phần 4 - 5: bảng đối chiếu, bài tập |
Phần 1: Excel
Mở FMCG_BanHang_2024_2025.xlsx bằng Excel. Bấm vào bảng, nhấn Ctrl + T để chuyển thành Excel Table, đặt tên bảng là BanHang (tab Table Design > Table Name).
1.1 Tổng hợp có điều kiện
| Hàm | Ý nghĩa | Ví dụ |
|---|---|---|
SUMIFS(cột_cộng, cột_đk1, đk1, ...) | Cộng có điều kiện | Doanh thu của Miền Nam |
COUNTIFS(cột_đk1, đk1, ...) | Đếm có điều kiện | Số dòng có khuyến mãi |
AVERAGEIFS(cột_tb, cột_đk1, đk1, ...) | Trung bình có điều kiện | Số thùng trung bình mỗi dòng ở kênh MT |
Ví dụ: doanh thu năm 2025 của Miền Nam, kênh GT.
=SUMIFS(BanHang[Doanh thu],
BanHang[Vùng], "Miền Nam",
BanHang[Kênh], "GT",
BanHang[Ngày đặt], ">=" & DATE(2025,1,1),
BanHang[Ngày đặt], "<=" & DATE(2025,12,31))
1.2 Tra cứu với XLOOKUP
Bảng BanHang không có Giá niêm yết 2023. Cột này nằm ở bảng danh mục sản phẩm DimSanPham.
- Mở
data/goc/DimSanPham.csvbằng Excel. Copy toàn bộ sang một sheet mới trong file bán hàng, đặt tên sheet làDimSanPham. Chuyển thành Table tênSanPham. - Trong bảng BanHang, thêm cột Giá niêm yết 2023:
=XLOOKUP([@[Mã SKU]], SanPham[MaSKU], SanPham[GiaNiemYet2023], "Không tìm thấy")
Nếu Excel của bạn chưa có XLOOKUP (bản 2016/2019), dùng:
=INDEX(SanPham[GiaNiemYet2023], MATCH([@[Mã SKU]], SanPham[MaSKU], 0))
Điều kiện để tra cứu được: hai bảng có cột chung (Mã SKU), và cột đó ở bảng danh mục không trùng (mỗi mã chỉ một dòng). Ghi nhớ điều kiện này. Trong Power BI, nó chính là điều kiện để tạo quan hệ (relationship) giữa 2 bảng.
1.3 PivotTable
- Chọn bảng BanHang > Insert > PivotTable > New Worksheet.
- Kéo Ngành hàng vào Rows, Ngày đặt vào Columns. Excel tự nhóm theo Năm, Quý; nếu không, chuột phải > Group > chọn Years.
- Kéo Doanh thu vào Values.
Bạn vừa làm đúng việc mà visual Matrix của Power BI làm ở Ngày 1.
Phần 2: SQL
2.1 Chuẩn bị
- Tải và cài DB Browser for SQLite (miễn phí). Tìm “DB Browser for SQLite download”.
- Mở phần mềm > Open Database > chọn
data/tuan-01/fmcg_saomai.sqlite. - Tab Database Structure liệt kê 7 bảng. Tab Execute SQL là nơi gõ truy vấn, bấm F5 hoặc Ctrl + Enter để chạy.
2.2 Các bảng trong cơ sở dữ liệu
Khác với file Excel phẳng, cơ sở dữ liệu tách thông tin thành nhiều bảng:
- Bảng Fact (sự kiện):
FactDonHang,FactTraHang. Mỗi dòng là một sự kiện đã xảy ra, có con số đo lường (số lượng, đơn giá). Rất nhiều dòng. - Bảng Dim (dimension, danh mục):
DimSanPham,DimCuaHang,DimNPP,DimNhanVien,DimKhuyenMai. Mô tả ai, cái gì, ở đâu. Ít dòng. - Fact nối với Dim qua khóa. Ví dụ
FactDonHang.MaSKU→DimSanPham.MaSKU.
FactDonHang không có cột Doanh thu. Doanh thu phải tự tính:
Doanh thu = SoLuong * DonGia * (1 - TyLeCK)
2.3 Truy vấn mẫu
Ví dụ 1: SELECT, WHERE, ORDER BY, LIMIT. 10 dòng đơn hàng lớn nhất tháng 1/2025:
SELECT MaDonHang, NgayDat, MaCH, MaSKU, SoLuong, DonGia, TyLeCK,
SoLuong * DonGia * (1 - TyLeCK) AS DoanhThu
FROM FactDonHang
WHERE NgayDat BETWEEN '2025-01-01' AND '2025-01-31'
ORDER BY DoanhThu DESC
LIMIT 10;
Ví dụ 2: GROUP BY. Số cửa hàng theo kênh:
SELECT Kenh, COUNT(*) AS SoCuaHang
FROM DimCuaHang
GROUP BY Kenh;
Ví dụ 3: JOIN. Doanh thu 2025 theo ngành hàng:
SELECT s.NganhHang,
ROUND(SUM(f.SoLuong * f.DonGia * (1 - f.TyLeCK))) AS DoanhThu
FROM FactDonHang AS f
JOIN DimSanPham AS s ON f.MaSKU = s.MaSKU
WHERE f.NgayDat BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY s.NganhHang
ORDER BY DoanhThu DESC;
So sánh kết quả với bài tập 5 của Ngày 1: phải khớp từng đồng.
2.4 Các kiểu JOIN
LEFT JOIN + IS NULL (anti join) dùng để bắt lỗi dữ liệu: tìm các dòng ở bảng A không có “bạn” ở bảng B. Power Query có sẵn kiểu Merge này với tên Left Anti (tuần 3).
Phần 3: Từ Excel/SQL sang Power BI
| Bạn đã biết | Trong Power BI | Học ở |
|---|---|---|
Lọc dòng (AutoFilter / WHERE) | Filter trong Power Query, Filters pane, slicer | Tuần 1 - 2 |
Thêm cột tính (=A2*B2 / SELECT a*b AS ...) | Custom Column (Power Query) hoặc Calculated Column (DAX) | Tuần 2, 6 |
XLOOKUP / JOIN để lấy thêm cột | Merge Queries (Power Query) hoặc Relationship (Model) | Tuần 3, 5 |
Nối 2 bảng cùng cấu trúc / UNION ALL | Append Queries | Tuần 3 |
PivotTable / GROUP BY | Matrix visual, Group By (Power Query), measure DAX | Tuần 1, 2, 6 |
SUMIFS | Measure với CALCULATE | Tuần 7 |
| Unpivot bằng tay | Unpivot Columns | Tuần 2 |
LEFT JOIN ... IS NULL | Merge kiểu Left Anti | Tuần 3 |
Phần 4: Bài tập tự làm
Excel
SUMIFS, COUNTIFS, AVERAGEIFS. Trên bảng BanHang (Excel), viết công thức tính:
- Doanh thu năm 2025, Miền Nam, kênh GT.
- Số dòng có Mã CTKM =
KM2505. - Số thùng trung bình mỗi dòng của kênh MT (cả 2 năm), làm tròn 2 chữ số.
- 10.369.061.960 (công thức ở mục 1.1).
- 4.027
=COUNTIFS(BanHang[Mã CTKM], "KM2505") - 4,05. Kênh GT là 2,55.
=ROUND(AVERAGEIFS(BanHang[Số lượng (thùng)], BanHang[Kênh], "MT"), 2)
XLOOKUP và giá tăng. Sau khi thêm cột Giá niêm yết 2023 (mục 1.2):
- Thêm cột Tăng giá? = “Có” nếu Đơn giá > Giá niêm yết 2023, ngược lại “Không”.
- Đếm số dòng “Có”.
- Lọc sản phẩm Hạt nêm Hương Quê 400g. Có mấy mức đơn giá khác nhau trong 2024 - 2025? Đoán xem công ty đã tăng giá vào thời điểm nào.
- Công thức:
=IF([@[Đơn giá]] > [@[Giá niêm yết 2023]], "Có", "Không") - 59.157 dòng “Có” trên tổng 77.142 dòng.
- Hạt nêm có 3 mức giá:
| Đơn giá | Giai đoạn |
|---|---|
| 760.000 | Trước 01/07/2024 |
| 798.000 | Từ 01/07/2024 (tăng 5%) |
| 830.000 | Từ 01/07/2025 (tăng thêm 4%) |
Cách tìm thời điểm tăng giá: lọc theo từng mức đơn giá, xem ngày đặt nhỏ nhất. Với SQL dùng MIN(NgayDat) ... GROUP BY DonGia.
PivotTable. Làm PivotTable Doanh thu theo Ngành hàng x Năm. Ngành nào tăng trưởng nhanh nhất từ 2024 sang 2025?
| Ngành hàng | 2024 | 2025 | Tăng trưởng |
|---|---|---|---|
| Chăm sóc nhà cửa & cá nhân | 9.371.639.250 | 11.741.414.710 | +25,3% |
| Gia vị & thực phẩm khô | 8.771.672.530 | 10.818.197.070 | +23,3% |
| Sữa & sản phẩm từ sữa | 7.446.389.330 | 9.021.083.360 | +21,1% |
| Bánh kẹo | 7.607.029.030 | 8.593.642.490 | +13,0% |
| Nước giải khát | 5.950.242.820 | 7.270.965.540 | +22,2% |
Nhanh nhất: Chăm sóc nhà cửa & cá nhân. Tăng trưởng thấp nhất là Bánh kẹo.
Muốn biết vì sao Bánh kẹo tăng chậm, thêm Tháng vào PivotTable (Rows = Tháng, lọc Ngành = Bánh kẹo). Bạn sẽ thấy các tháng bình thường vẫn tăng khoảng 20%, nhưng các tháng mùa Tết (tháng 1, 12) gần như đi ngang so với năm trước. Bánh kẹo phụ thuộc mạnh vào Tết, nên tăng trưởng cả năm bị kéo xuống. Đây là kiểu “đào sâu” (drill down) bạn sẽ làm rất nhiều trong Power BI.
SQL
GROUP BY và JOIN. Viết truy vấn SQL:
- Số cửa hàng theo loại cửa hàng (
DimCuaHang.LoaiCH), sắp giảm dần. - Doanh thu 2025 theo Vùng và Kênh. Gợi ý: JOIN
FactDonHangvớiDimCuaHang. - Số đơn hàng và giá trị trung bình mỗi đơn năm 2025 theo Kênh.
1. Số cửa hàng theo loại:
SELECT LoaiCH, COUNT(*) AS SoCuaHang
FROM DimCuaHang
GROUP BY LoaiCH
ORDER BY SoCuaHang DESC;
| LoaiCH | SoCuaHang |
|---|---|
| Tạp hóa | 280 |
| Sạp chợ | 79 |
| Siêu thị mini | 50 |
| Cửa hàng tiện lợi | 40 |
| Siêu thị | 21 |
2. Doanh thu 2025 theo Vùng và Kênh:
SELECT c.Vung, c.Kenh,
ROUND(SUM(f.SoLuong * f.DonGia * (1 - f.TyLeCK))) AS DoanhThu
FROM FactDonHang f
JOIN DimCuaHang c ON f.MaCH = c.MaCH
WHERE f.NgayDat BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY c.Vung, c.Kenh
ORDER BY c.Vung, c.Kenh;Kết quả khớp với Matrix ở bài 2 của Review 1. Ví dụ Miền Nam - GT = 10.369.061.960.
3. Số đơn và giá trị trung bình mỗi đơn:
SELECT c.Kenh,
COUNT(DISTINCT f.MaDonHang) AS SoDon,
ROUND(SUM(f.SoLuong * f.DonGia * (1 - f.TyLeCK))
/ COUNT(DISTINCT f.MaDonHang)) AS GiaTriTBDon
FROM FactDonHang f
JOIN DimCuaHang c ON f.MaCH = c.MaCH
WHERE f.NgayDat BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY c.Kenh;
| Kenh | SoDon | GiaTriTBDon |
|---|---|---|
| GT | 6.749 | khoảng 3.836.977 |
| MT | 3.228 | khoảng 6.675.820 |
Mỗi đơn MT có giá trị gần gấp đôi đơn GT. Điều này giải thích vì sao MT chỉ có 24% số cửa hàng nhưng chiếm khoảng 45% doanh thu.
Bắt lỗi dữ liệu bằng anti join.
- Bảng
FactTraHangcó bao nhiêu dòng màMaDonHangkhông tồn tại trongFactDonHang? - Có cửa hàng nào không có đơn hàng nào từ 01/10/2025 trở đi không? Liệt kê mã và tên. Gợi ý:
NOT EXISTShoặcLEFT JOIN ... IS NULL.
1. Dòng trả hàng “mồ côi”: 12 dòng.
SELECT t.*
FROM FactTraHang t
LEFT JOIN (SELECT DISTINCT MaDonHang FROM FactDonHang) d
ON t.MaDonHang = d.MaDonHang
WHERE d.MaDonHang IS NULL;Chú ý: phải lấy DISTINCT MaDonHang trước khi JOIN. Một đơn có nhiều dòng, JOIN thẳng sẽ nhân bản dòng trả hàng. Tuy với anti join kết quả đếm vẫn đúng, đây là thói quen tốt cần giữ.
Trong thực tế, các dòng này cần được báo lại cho bộ phận kho hoặc kế toán để kiểm tra (nhập sai mã đơn?).
2. Cửa hàng không có đơn từ 01/10/2025: 6 cửa hàng.
SELECT c.MaCH, c.TenCH, c.LoaiCH
FROM DimCuaHang c
WHERE NOT EXISTS (
SELECT 1 FROM FactDonHang f
WHERE f.MaCH = c.MaCH AND f.NgayDat >= '2025-10-01'
);
| MaCH | TenCH | LoaiCH |
|---|---|---|
| CH0064 | Sạp chợ Ông Giang | Sạp chợ |
| CH0066 | Tạp hóa Anh Nam | Tạp hóa |
| CH0145 | Tạp hóa Ông Yến | Tạp hóa |
| CH0252 | Tạp hóa Bà Thảo | Tạp hóa |
| CH0255 | Tạp hóa Chị Dũng | Tạp hóa |
| CH0338 | Tạp hóa Anh Việt | Tạp hóa |
Đây là các cửa hàng nhỏ, đặt hàng ít. Đội bán hàng nên ghé thăm lại. Phân tích “khách hàng ngừng mua” sẽ được làm bằng DAX ở tuần 8.
Phần 5: Tự đánh giá
- Tôi viết được SUMIFS có điều kiện ngày
- Tôi dùng được XLOOKUP và biết điều kiện để tra cứu được
- Tôi viết được JOIN + GROUP BY để tổng hợp theo cột của bảng danh mục
- Tôi phân biệt được bảng Fact và bảng Dim
- Tôi biết Merge, Append, Relationship, Matrix tương ứng với thao tác nào trong Excel/SQL