Chuyển tới nội dung chính

Bài 12: Dự Án Tổng Hợp – Xây Dựng CSDL Quản Lý Bán Hàng

Giai đoạn 3 – Nâng Cao · Mục tiêu: Áp dụng toàn bộ kiến thức đã học để xây dựng một hệ thống CSDL quản lý bán hàng hoàn chỉnh từ đầu.


1. Mô tả bài toán

Xây dựng CSDL cho cửa hàng TechShop kinh doanh thiết bị công nghệ:

Yêu cầu nghiệp vụ

  • Quản lý khách hàng: thông tin cá nhân, hạng thành viên.
  • Quản lý sản phẩm: theo danh mục, theo nhà cung cấp.
  • Quản lý nhân viên: phòng ban, chức vụ.
  • Quản lý đơn hàng: đặt hàng, chi tiết đơn, trạng thái.
  • Báo cáo: doanh thu, top sản phẩm, hiệu suất nhân viên.

2. Thiết kế CSDL

2.1. Sơ đồ quan hệ

DanhMuc (1) ──── (N) SanPham (N) ──── (1) NhaCungCap

│ (N)

PhongBan (1) ── (N) NhanVien ChiTietDonHang
│ │
│ (1) (N) │ (N)
│ │
DonHang (1) ────── (N) ChiTietDonHang

│ (N)

KhachHang (1)

2.2. Tạo CSDL và các bảng

-- ====================================
-- TẠO CƠ SỞ DỮ LIỆU
-- ====================================
CREATE DATABASE IF NOT EXISTS TechShop;
USE TechShop;

-- Đảm bảo hỗ trợ tiếng Việt
ALTER DATABASE TechShop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- ====================================
-- BẢNG DANH MỤC SẢN PHẨM
-- ====================================
CREATE TABLE DanhMuc (
MaDM INT AUTO_INCREMENT PRIMARY KEY,
TenDM VARCHAR(100) NOT NULL UNIQUE,
MoTa TEXT
);

-- ====================================
-- BẢNG NHÀ CUNG CẤP
-- ====================================
CREATE TABLE NhaCungCap (
MaNCC INT AUTO_INCREMENT PRIMARY KEY,
TenNCC VARCHAR(200) NOT NULL,
DiaChi VARCHAR(255),
DienThoai VARCHAR(15),
Email VARCHAR(255),
Website VARCHAR(255)
);

-- ====================================
-- BẢNG SẢN PHẨM
-- ====================================
CREATE TABLE SanPham (
MaSP INT AUTO_INCREMENT PRIMARY KEY,
TenSP VARCHAR(200) NOT NULL,
MaDM INT,
MaNCC INT,
GiaNhap DECIMAL(15, 2) NOT NULL CHECK (GiaNhap >= 0),
GiaBan DECIMAL(15, 2) NOT NULL CHECK (GiaBan >= 0),
SoLuongTon INT DEFAULT 0 CHECK (SoLuongTon >= 0),
MoTa TEXT,
NgayNhap DATE DEFAULT (CURRENT_DATE),
TrangThai ENUM('Đang bán', 'Ngừng bán', 'Hết hàng') DEFAULT 'Đang bán',

FOREIGN KEY (MaDM) REFERENCES DanhMuc(MaDM),
FOREIGN KEY (MaNCC) REFERENCES NhaCungCap(MaNCC)
);

-- ====================================
-- BẢNG PHÒNG BAN
-- ====================================
CREATE TABLE PhongBan (
MaPB INT AUTO_INCREMENT PRIMARY KEY,
TenPB VARCHAR(100) NOT NULL UNIQUE,
DiaDiem VARCHAR(200)
);

-- ====================================
-- BẢNG NHÂN VIÊN
-- ====================================
CREATE TABLE NhanVien (
MaNV INT AUTO_INCREMENT PRIMARY KEY,
HoTen VARCHAR(100) NOT NULL,
GioiTinh ENUM('Nam', 'Nữ') DEFAULT 'Nam',
NgaySinh DATE,
DienThoai VARCHAR(15),
Email VARCHAR(255) UNIQUE,
MaPB INT,
ChucVu VARCHAR(50) DEFAULT 'Nhân viên',
Luong DECIMAL(12, 2) CHECK (Luong > 0),
NgayVaoLam DATE DEFAULT (CURRENT_DATE),

FOREIGN KEY (MaPB) REFERENCES PhongBan(MaPB)
);

-- ====================================
-- BẢNG KHÁCH HÀNG
-- ====================================
CREATE TABLE KhachHang (
MaKH INT AUTO_INCREMENT PRIMARY KEY,
HoTen VARCHAR(100) NOT NULL,
GioiTinh ENUM('Nam', 'Nữ'),
DienThoai VARCHAR(15) UNIQUE,
Email VARCHAR(255) UNIQUE,
DiaChi VARCHAR(255),
NgayDangKy DATE DEFAULT (CURRENT_DATE),
HangTV ENUM('Thường', 'Bạc', 'Vàng', 'Kim cương') DEFAULT 'Thường',
TongChiTieu DECIMAL(15, 2) DEFAULT 0
);

-- ====================================
-- BẢNG ĐƠN HÀNG
-- ====================================
CREATE TABLE DonHang (
MaDH INT AUTO_INCREMENT PRIMARY KEY,
MaKH INT NOT NULL,
MaNV INT,
NgayDat DATETIME DEFAULT NOW(),
TongTien DECIMAL(15, 2) DEFAULT 0,
GiamGia DECIMAL(5, 2) DEFAULT 0 CHECK (GiamGia >= 0 AND GiamGia <= 100),
ThanhToan DECIMAL(15, 2) DEFAULT 0,
PhuongThuc ENUM('Tiền mặt', 'Chuyển khoản', 'Thẻ', 'Trả góp') DEFAULT 'Tiền mặt',
TrangThai ENUM('Chờ xác nhận', 'Đã xác nhận', 'Đang giao', 'Hoàn thành', 'Đã huỷ')
DEFAULT 'Chờ xác nhận',
GhiChu TEXT,

FOREIGN KEY (MaKH) REFERENCES KhachHang(MaKH),
FOREIGN KEY (MaNV) REFERENCES NhanVien(MaNV)
);

-- ====================================
-- BẢNG CHI TIẾT ĐƠN HÀNG
-- ====================================
CREATE TABLE ChiTietDonHang (
MaCT INT AUTO_INCREMENT PRIMARY KEY,
MaDH INT NOT NULL,
MaSP INT NOT NULL,
SoLuong INT NOT NULL CHECK (SoLuong > 0),
DonGia DECIMAL(15, 2) NOT NULL,
ThanhTien DECIMAL(15, 2),

FOREIGN KEY (MaDH) REFERENCES DonHang(MaDH) ON DELETE CASCADE,
FOREIGN KEY (MaSP) REFERENCES SanPham(MaSP)
);

-- ====================================
-- TẠO INDEX
-- ====================================
CREATE INDEX idx_sp_danhmuc ON SanPham(MaDM);
CREATE INDEX idx_sp_ncc ON SanPham(MaNCC);
CREATE INDEX idx_dh_ngaydat ON DonHang(NgayDat);
CREATE INDEX idx_dh_trangthai ON DonHang(TrangThai);
CREATE INDEX idx_dh_khach ON DonHang(MaKH);
CREATE INDEX idx_ct_donhang ON ChiTietDonHang(MaDH);
CREATE INDEX idx_nv_phongban ON NhanVien(MaPB);

3. Nhập dữ liệu mẫu

-- ====================================
-- DỮ LIỆU MẪU
-- ====================================

-- Danh mục
INSERT INTO DanhMuc (TenDM, MoTa) VALUES
('Điện thoại', 'Smartphone các hãng'),
('Laptop', 'Máy tính xách tay'),
('Máy tính bảng', 'iPad và các tablet'),
('Phụ kiện', 'Tai nghe, ốp lưng, sạc dự phòng'),
('Đồng hồ thông minh', 'Smartwatch các hãng');

-- Nhà cung cấp
INSERT INTO NhaCungCap (TenNCC, DiaChi, DienThoai, Email) VALUES
('Apple Vietnam', 'Quận 1, TP.HCM', '02838001000', 'contact@apple.vn'),
('Samsung Việt Nam', 'Thủ Đức, TP.HCM', '02839001000', 'info@samsung.vn'),
('Xiaomi Việt Nam', 'Quận 7, TP.HCM', '02837001000', 'support@xiaomi.vn');

-- Phòng ban
INSERT INTO PhongBan (TenPB, DiaDiem) VALUES
('Kinh doanh', 'Tầng 1'),
('Kỹ thuật', 'Tầng 2'),
('Kho vận', 'Tầng trệt'),
('Nhân sự', 'Tầng 3');

-- Nhân viên
INSERT INTO NhanVien (HoTen, GioiTinh, NgaySinh, DienThoai, Email, MaPB, ChucVu, Luong, NgayVaoLam) VALUES
('Nguyễn Văn An', 'Nam', '1990-05-15', '0901234567', 'an@techshop.vn', 1, 'Quản lý', 25000000, '2019-01-15'),
('Trần Thị Bình', 'Nữ', '1995-08-20', '0912345678', 'binh@techshop.vn', 1, 'Nhân viên', 15000000, '2021-06-01'),
('Lê Minh Châu', 'Nam', '1992-03-10', '0923456789', 'chau@techshop.vn', 1, 'Nhân viên', 14000000, '2022-03-10'),
('Phạm Đức Dũng', 'Nam', '1988-11-25', '0934567890', 'dung@techshop.vn', 2, 'Trưởng nhóm', 22000000, '2018-09-01'),
('Hoàng Thị Em', 'Nữ', '1997-07-08', '0945678901', 'em@techshop.vn', 3, 'Nhân viên', 12000000, '2023-01-15'),
('Võ Văn Phong', 'Nam', '1993-12-30', '0956789012', 'phong@techshop.vn', 1, 'Nhân viên', 13500000, '2022-07-20'),
('Đặng Thuỳ Giang', 'Nữ', '1994-04-18', '0967890123', 'giang@techshop.vn', 4, 'Trưởng nhóm', 20000000, '2020-03-01');

-- Sản phẩm
INSERT INTO SanPham (TenSP, MaDM, MaNCC, GiaNhap, GiaBan, SoLuongTon, MoTa) VALUES
('iPhone 15 Pro Max', 1, 1, 28000000, 33990000, 50, 'iPhone 15 Pro Max 256GB'),
('iPhone 15', 1, 1, 20000000, 24990000, 80, 'iPhone 15 128GB'),
('Samsung Galaxy S24 Ultra', 1, 2, 25000000, 31990000, 35, 'Galaxy S24 Ultra 256GB'),
('Samsung Galaxy A55', 1, 2, 7000000, 9990000, 120, 'Galaxy A55 5G 128GB'),
('MacBook Air M3', 2, 1, 24000000, 28490000, 25, 'MacBook Air 13 M3 8GB/256GB'),
('MacBook Pro M3 Pro', 2, 1, 38000000, 45990000, 15, 'MacBook Pro 14 M3 Pro 18GB/512GB'),
('iPad Air M2', 3, 1, 14000000, 16990000, 40, 'iPad Air 11 M2 128GB WiFi'),
('iPad Pro M4', 3, 1, 25000000, 30990000, 20, 'iPad Pro 11 M4 256GB WiFi'),
('AirPods Pro 2', 4, 1, 4500000, 5990000, 150, 'AirPods Pro 2 USB-C'),
('Samsung Galaxy Buds3 Pro', 4, 2, 3500000, 4990000, 80, 'Galaxy Buds3 Pro'),
('Apple Watch Series 9', 5, 1, 8500000, 10990000, 45, 'Apple Watch S9 GPS 41mm'),
('Xiaomi Redmi Note 13', 1, 3, 3500000, 4990000, 200, 'Redmi Note 13 128GB'),
('Xiaomi Pad 6', 3, 3, 6000000, 7990000, 60, 'Xiaomi Pad 6 128GB');

-- Khách hàng
INSERT INTO KhachHang (HoTen, GioiTinh, DienThoai, Email, DiaChi, NgayDangKy, HangTV, TongChiTieu) VALUES
('Trương Văn Hải', 'Nam', '0971111111', 'hai@gmail.com', '12 Nguyễn Huệ, Q1, HCM', '2023-01-10', 'Vàng', 85000000),
('Lý Thị Kim', 'Nữ', '0972222222', 'kim@gmail.com', '34 Lê Lợi, Q1, HCM', '2023-03-15', 'Bạc', 35000000),
('Ngô Quốc Long', 'Nam', '0973333333', 'long@gmail.com', '56 Hai Bà Trưng, Q3, HCM', '2023-06-20', 'Kim cương', 150000000),
('Đinh Thuý Mai', 'Nữ', '0974444444', 'mai@gmail.com', '78 Pasteur, Q1, HCM', '2024-01-05', 'Thường', 8000000),
('Bùi Thanh Nam', 'Nam', '0975555555', 'nam@gmail.com', '90 CMT8, Q3, HCM', '2024-03-12', 'Bạc', 42000000),
('Phan Ngọc Oanh', 'Nữ', '0976666666', 'oanh@gmail.com', '11 Nguyễn Trãi, Q5, HCM', '2024-06-01', 'Thường', 5000000),
('Cao Minh Phúc', 'Nam', '0977777777', 'phuc@gmail.com', '22 Võ Văn Tần, Q3, HCM', '2024-08-15', 'Thường', 0),
('Huỳnh Bảo Quân', 'Nam', '0978888888', 'quan@gmail.com', '33 Điện Biên Phủ, BT, HCM', '2024-09-01', 'Vàng', 70000000);

-- Đơn hàng
INSERT INTO DonHang (MaKH, MaNV, NgayDat, TongTien, GiamGia, ThanhToan, PhuongThuc, TrangThai) VALUES
(1, 1, '2024-10-01 09:30:00', 33990000, 5, 32290500, 'Chuyển khoản', 'Hoàn thành'),
(2, 2, '2024-10-05 14:15:00', 24990000, 0, 24990000, 'Tiền mặt', 'Hoàn thành'),
(3, 1, '2024-10-10 11:00:00', 77970000, 10, 70173000, 'Thẻ', 'Hoàn thành'),
(1, 3, '2024-11-01 10:00:00', 5990000, 5, 5690500, 'Tiền mặt', 'Hoàn thành'),
(5, 2, '2024-11-10 16:30:00', 28490000, 0, 28490000, 'Trả góp', 'Hoàn thành'),
(4, 6, '2024-11-15 09:00:00', 16990000, 0, 16990000, 'Chuyển khoản', 'Hoàn thành'),
(3, 1, '2024-11-20 13:45:00', 45990000, 10, 41391000, 'Thẻ', 'Hoàn thành'),
(6, 3, '2024-12-01 10:30:00', 9990000, 0, 9990000, 'Tiền mặt', 'Đang giao'),
(8, 2, '2024-12-05 15:00:00', 42980000, 5, 40831000, 'Chuyển khoản', 'Đã xác nhận'),
(1, 1, '2024-12-10 11:30:00', 10990000, 5, 10440500, 'Tiền mặt', 'Chờ xác nhận'),
(5, 6, '2024-12-12 14:00:00', 4990000, 0, 4990000, 'Tiền mặt', 'Hoàn thành'),
(7, 2, '2024-12-15 09:15:00', 24990000, 0, 24990000, 'Chuyển khoản', 'Chờ xác nhận');

-- Chi tiết đơn hàng
INSERT INTO ChiTietDonHang (MaDH, MaSP, SoLuong, DonGia, ThanhTien) VALUES
(1, 1, 1, 33990000, 33990000),
(2, 2, 1, 24990000, 24990000),
(3, 1, 1, 33990000, 33990000),
(3, 6, 1, 45990000, 45990000),
(4, 9, 1, 5990000, 5990000),
(5, 5, 1, 28490000, 28490000),
(6, 7, 1, 16990000, 16990000),
(7, 6, 1, 45990000, 45990000),
(8, 4, 1, 9990000, 9990000),
(9, 3, 1, 31990000, 31990000),
(9, 11, 1, 10990000, 10990000),
(10, 11, 1, 10990000, 10990000),
(11, 12, 1, 4990000, 4990000),
(12, 2, 1, 24990000, 24990000);

4. Tạo View hữu ích

-- ====================================
-- CÁC VIEW BÁO CÁO
-- ====================================

-- View: Đơn hàng chi tiết (dùng thường xuyên)
CREATE VIEW vw_DonHangChiTiet AS
SELECT
dh.MaDH,
kh.HoTen AS KhachHang,
kh.DienThoai AS SDT_KH,
kh.HangTV,
nv.HoTen AS NhanVienBan,
dh.NgayDat,
sp.TenSP,
ct.SoLuong,
ct.DonGia,
ct.ThanhTien,
dh.GiamGia,
dh.ThanhToan,
dh.PhuongThuc,
dh.TrangThai
FROM ChiTietDonHang AS ct
INNER JOIN DonHang AS dh ON ct.MaDH = dh.MaDH
INNER JOIN KhachHang AS kh ON dh.MaKH = kh.MaKH
INNER JOIN NhanVien AS nv ON dh.MaNV = nv.MaNV
INNER JOIN SanPham AS sp ON ct.MaSP = sp.MaSP;

-- View: Tồn kho
CREATE VIEW vw_TonKho AS
SELECT
sp.MaSP,
sp.TenSP,
dm.TenDM AS DanhMuc,
ncc.TenNCC AS NhaCungCap,
sp.GiaNhap,
sp.GiaBan,
sp.SoLuongTon,
sp.GiaBan * sp.SoLuongTon AS GiaTriTonKho,
sp.TrangThai
FROM SanPham AS sp
LEFT JOIN DanhMuc AS dm ON sp.MaDM = dm.MaDM
LEFT JOIN NhaCungCap AS ncc ON sp.MaNCC = ncc.MaNCC;

5. Các câu truy vấn báo cáo

Báo cáo doanh thu

-- Doanh thu theo tháng
SELECT
DATE_FORMAT(NgayDat, '%m/%Y') AS Thang,
COUNT(*) AS SoDon,
SUM(ThanhToan) AS DoanhThu,
ROUND(AVG(ThanhToan), 0) AS TBMoiDon
FROM DonHang
WHERE TrangThai = 'Hoàn thành'
GROUP BY DATE_FORMAT(NgayDat, '%m/%Y')
ORDER BY MIN(NgayDat);

-- Doanh thu theo danh mục sản phẩm
SELECT
dm.TenDM,
COUNT(ct.MaCT) AS SoLuongBan,
SUM(ct.ThanhTien) AS DoanhThu,
ROUND(SUM(ct.ThanhTien) / (SELECT SUM(ThanhToan) FROM DonHang WHERE TrangThai = 'Hoàn thành') * 100, 1) AS PhanTramDT
FROM ChiTietDonHang AS ct
INNER JOIN SanPham AS sp ON ct.MaSP = sp.MaSP
INNER JOIN DanhMuc AS dm ON sp.MaDM = dm.MaDM
INNER JOIN DonHang AS dh ON ct.MaDH = dh.MaDH
WHERE dh.TrangThai = 'Hoàn thành'
GROUP BY dm.TenDM
ORDER BY DoanhThu DESC;

Top sản phẩm bán chạy

SELECT
sp.TenSP,
dm.TenDM,
SUM(ct.SoLuong) AS TongBan,
SUM(ct.ThanhTien) AS TongDoanhThu,
sp.SoLuongTon AS ConLai
FROM ChiTietDonHang AS ct
INNER JOIN SanPham AS sp ON ct.MaSP = sp.MaSP
INNER JOIN DanhMuc AS dm ON sp.MaDM = dm.MaDM
INNER JOIN DonHang AS dh ON ct.MaDH = dh.MaDH
WHERE dh.TrangThai = 'Hoàn thành'
GROUP BY sp.MaSP
ORDER BY TongBan DESC
LIMIT 5;

‍ Hiệu suất nhân viên

SELECT
nv.HoTen,
nv.ChucVu,
COUNT(dh.MaDH) AS SoDonXuLy,
SUM(dh.ThanhToan) AS TongDoanhThu,
ROUND(AVG(dh.ThanhToan), 0) AS TBMoiDon,
CASE
WHEN SUM(dh.ThanhToan) >= 100000000 THEN ' Xuất sắc'
WHEN SUM(dh.ThanhToan) >= 50000000 THEN ' Tốt'
ELSE ' Cần cải thiện'
END AS DanhGia
FROM NhanVien AS nv
LEFT JOIN DonHang AS dh ON nv.MaNV = dh.MaNV AND dh.TrangThai = 'Hoàn thành'
WHERE nv.MaPB = 1 -- Phòng Kinh doanh
GROUP BY nv.MaNV
ORDER BY TongDoanhThu DESC;

Phân tích khách hàng

-- Khách hàng VIP (chi tiêu nhiều nhất)
SELECT
kh.HoTen,
kh.HangTV,
COUNT(dh.MaDH) AS SoDon,
SUM(dh.ThanhToan) AS TongChiTieu,
MAX(dh.NgayDat) AS LanMuaGanNhat,
DATEDIFF(CURDATE(), MAX(dh.NgayDat)) AS SoNgayChuaMua
FROM KhachHang AS kh
LEFT JOIN DonHang AS dh ON kh.MaKH = dh.MaKH AND dh.TrangThai = 'Hoàn thành'
GROUP BY kh.MaKH
ORDER BY TongChiTieu DESC;

-- Khách hàng chưa mua lại trong 60 ngày
SELECT kh.HoTen, kh.DienThoai, kh.HangTV
FROM KhachHang AS kh
WHERE NOT EXISTS (
SELECT 1 FROM DonHang AS dh
WHERE dh.MaKH = kh.MaKH
AND dh.NgayDat >= DATE_SUB(NOW(), INTERVAL 60 DAY)
AND dh.TrangThai = 'Hoàn thành'
);

Cảnh báo tồn kho

-- Sản phẩm sắp hết hàng (tồn kho ≤ 20)
SELECT
TenSP,
DanhMuc,
NhaCungCap,
SoLuongTon,
CASE
WHEN SoLuongTon = 0 THEN ' Hết hàng'
WHEN SoLuongTon <= 10 THEN ' Cực thấp'
ELSE ' Thấp'
END AS MucCanhBao
FROM vw_TonKho
WHERE SoLuongTon <= 20 AND TrangThai = 'Đang bán'
ORDER BY SoLuongTon ASC;

6. Bài tập nâng cao

Bài 1: Truy vấn cơ bản

  1. Liệt kê tất cả sản phẩm Apple có giá > 20 triệu.
  2. Tìm đơn hàng trong tháng 11/2024 đã hoàn thành.
  3. Liệt kê khách hàng hạng Vàng trở lên.

Bài 2: JOIN & GROUP BY

  1. Thống kê doanh thu theo nhà cung cấp.
  2. Tìm danh mục có lợi nhuận cao nhất (tổng GiaBan - GiaNhap).
  3. Liệt kê nhân viên chưa xử lý đơn hàng nào.

Bài 3: Subquery & CASE WHEN

  1. Tìm sản phẩm có giá cao hơn giá trung bình của danh mục đó.
  2. Cập nhật hạng thành viên khách hàng: Chi tiêu ≥ 100tr → Kim cương, ≥ 50tr → Vàng, ≥ 20tr → Bạc.
  3. Tạo báo cáo: mỗi tháng, so sánh doanh thu với tháng trước (tăng/giảm bao nhiêu %).

Bài 4: View & Tối ưu

  1. Tạo View tổng hợp Bảng điều khiển (Dashboard) hiển thị:
    • Tổng doanh thu tháng này
    • Số đơn hàng mới
    • Top 3 sản phẩm bán chạy
    • Số sản phẩm sắp hết hàng

Bài 5: Mở rộng hệ thống

  1. Thêm bảng KhuyenMai (mã, tên, loại giảm giá, giá trị, ngày bắt đầu, ngày kết thúc).
  2. Liên kết bảng KhuyenMai với DonHang.
  3. Viết truy vấn tìm đơn hàng được áp dụng khuyến mãi và tính số tiền tiết kiệm.

:::tip Tiếp theo học gì? Sau khi nắm vững SQL, bạn có thể tiếp tục với:

  • Stored Procedures & Triggers — Tự động hoá trong CSDL.
  • Transaction — Đảm bảo tính toàn vẹn dữ liệu.
  • NoSQL (MongoDB) — Cơ sở dữ liệu phi quan hệ.
  • ORM (Sequelize, Prisma) — Sử dụng SQL trong ứng dụng web. :::

Quay lại: Roadmap