Thử thách tư duy:
*"Ở Bài 4, một món đồ chỉ thuộc về duy nhất một danh mục (Ví dụ: Kiếm Kim Cương chỉ có thể là Vũ khí).
Nhưng bây giờ, hãy nghĩ về hệ thống của một Cửa hàng Mini/Siêu thị:
Một Hóa đơn (Order) có thể chứa nhiều Sản phẩm khác nhau (con đi siêu thị mua cả sữa, bánh và kẹo).
Ngược lại, một Sản phẩm (ví dụ: chai Coca-Cola) cũng xuất hiện trong nhiều Hóa đơn của nhiều khách hàng khác nhau.
Nếu ta dùng cách cũ: Nhét
san_pham_idvào bảnghoa_don, hoặc nhéthoa_don_idvào bảngsan_pham, thì đố các con làm sao lưu được khi một hóa đơn mua tới 10 món đồ? Dữ liệu sẽ bị trùng lặp vô tội vạ!"*
Để giải quyết mối quan hệ "rắc rối" Nhiều - Nhiều này, các kỹ sư dữ liệu không bao giờ nối trực tiếp 2 bảng với nhau. Họ sẽ thuê một "Ông mai mối" — chính là một cái Bảng thứ ba ở giữa (gọi là Bảng trung gian).
Cấu trúc sẽ như sau:
Bảng san_pham: Lưu thông tin sản phẩm (id, ten_san_pham, gia_ban).
Bảng hoa_don: Lưu thông tin chung của đơn hàng (id, ngay_mua, ten_khach_hang).
Bảng chi_tiet_hoa_don (Ông mai mối): Chỉ chứa 2 cột khóa ngoại quan trọng là hoa_don_id và san_pham_id (và có thể thêm cột so_luong_mua của món đó).
Hãy tạo một file mới tên là bai5_nhieu_nhieu.py và gõ đoạn code bản lề này để tạo ra bộ ba chiếc bảng quyền lực:
import sqlite3
ket_noi = sqlite3.connect("cua_hang_mini.db")
nguoi_tho = ket_noi.cursor()
nguoi_tho.execute("PRAGMA foreign_keys = ON;")
# 1. Tạo bảng SẢN PHẨM
nguoi_tho.execute("""
CREATE TABLE IF NOT EXISTS san_pham (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ten_san_pham TEXT NOT NULL,
gia_ban INTEGER NOT NULL
)
""")
# 2. Tạo bảng HÓA ĐƠN
nguoi_tho.execute("""
CREATE TABLE IF NOT EXISTS hoa_don (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ngay_mua TEXT NOT NULL,
ten_khach_hang TEXT NOT NULL
)
""")
# 3. Tạo BẢNG TRUNG GIAN chứa mối quan hệ Nhiều - Nhiều
nguoi_tho.execute("""
CREATE TABLE IF NOT EXISTS chi_tiet_hoa_don (
hoa_don_id INTEGER,
san_pham_id INTEGER,
so_luong_mua INTEGER DEFAULT 1,
PRIMARY KEY (hoa_don_id, san_pham_id), -- Khóa chính kết hợp (Composite Key)
FOREIGN KEY (hoa_don_id) REFERENCES hoa_don(id),
FOREIGN KEY (san_pham_id) REFERENCES san_pham(id)
)
""")
# --- NẠP DỮ LIỆU MẪU ---
# Thêm sản phẩm
nguoi_tho.executemany("INSERT INTO san_pham (ten_san_pham, gia_ban) VALUES (?, ?)", [
('Sữa tươi', 10000), # ID 1
('Bánh mì', 15000), # ID 2
('Kẹo mút', 5000) # ID 3
])
# Thêm 1 hóa đơn của chú Tuan mua đồ ngày hôm nay
nguoi_tho.execute("INSERT INTO hoa_don (ngay_mua, ten_khach_hang) VALUES ('2026-06-05', 'Chú Tuấn')")
# Giả sử hóa đơn này có ID là 1
# Bán hàng (Nạp vào bảng trung gian): Chú Tuấn mua 2 hộp Sữa (ID 1) và 3 cái Bánh mì (ID 2)
nguoi_tho.executemany("INSERT INTO chi_tiet_hoa_don (hoa_don_id, san_pham_id, so_luong_mua) VALUES (?, ?, ?)", [
(1, 1, 2), # Hóa đơn 1 mua Sản phẩm 1 (Số lượng: 2)
(1, 2, 3) # Hóa đơn 1 mua Sản phẩm 2 (Số lượng: 3)
])
ket_noi.commit()
print("Đã thiết lập thành công mối quan hệ Nhiều - Nhiều!")
JOIN — Đọc dữ liệu từ 3 bảngBây giờ, làm sao để in ra một cái hóa đơn hoàn chỉnh có chữ: "Chú Tuấn đã mua Sữa tươi với số lượng 2..."?
Vì dữ liệu bị chia làm 3 nơi, chúng ta sẽ dùng 2 lệnh INNER JOIN liên tiếp để nối một mạch từ bảng 1 sang bảng trung gian, rồi từ bảng trung gian bắc cầu sang bảng 2!
Hãy gõ tiếp đoạn code truy vấn này vào cuối file nhé:
print("\n--- IN HÓA ĐƠN CHI TIẾT TỪ 3 BẢNG ---")
cau_lenh_double_join = """
SELECT hoa_don.ten_khach_hang, hoa_don.ngay_mua, san_pham.ten_san_pham, chi_tiet_hoa_don.so_luong_mua, san_pham.gia_ban
FROM chi_tiet_hoa_don
INNER JOIN hoa_don ON chi_tiet_hoa_don.hoa_don_id = hoa_don.id
INNER JOIN san_pham ON chi_tiet_hoa_don.san_pham_id = san_pham.id
"""
nguoi_tho.execute(cau_lenh_double_join)
ket_qua = nguoi_tho.fetchall()
tong_tien_hoa_don = 0
print(f"Khách hàng: {ket_qua[0][0]} | Ngày mua: {ket_qua[0][1]}")
print("-" * 40)
for dong in ket_qua:
ten_sp = dong[2]
sl = dong[3]
gia = dong[4]
thanh_tien = sl * gia
tong_tien_hoa_don += thanh_tien
print(f"- {ten_sp} | SL: {sl} | Giá: {gia}đ -> Thành tiền: {thanh_tien}đ")
print("-" * 40)
print(f"💰 TỔNG CỘNG HÓA ĐƠN: {tong_tien_hoa_don}đ")
ket_noi.close()
Khi bấm Run, màn hình Terminal sẽ xuất ra một chiếc hóa đơn siêu thị đích thực:
--- IN HÓA ĐƠN CHI TIẾT TỪ 3 BẢNG ---
Khách hàng: Chú Tuấn | Ngày mua: 2026-06-05
----------------------------------------
- Sữa tươi | SL: 2 | Giá: 10000đ -> Thành tiền: 20000đ
- Bánh mì | SL: 3 | Giá: 15000đ -> Thành tiền: 45000đ
----------------------------------------
💰 TỔNG CỘNG HÓA ĐƠN: 65000đ
"Chúng ta thấy thế giới phần mềm vận hành chính là nhờ những bảng trung gian như thế này. Khi ta chơi game mua nhiều vật phẩm, khi ta lướt Facebook bấm Like nhiều bài viết... tất cả đều được lưu vào các bảng trung gian Nhiều - Nhiều giống như cái bảng
chi_tiet_hoa_donmà ta vừa tự tay lập trình!"