Bài tập SQL Bài tập SQL từ cơ bản đến nâng cao BÀI SQL Cơ bản – CHỦ ĐỀ: HỆ THỐNG BÁN HÀNG / ĐƠN HÀNG  Bảng users users ( id BIGINT PRIMARY KEY, username VARCHAR(50), full_name VARCHAR(100), email VARCHAR(100), department_id BIGINT, status INT,          -- 1: active, 0: inactive created_at TIMESTAMP )  Bảng departments departments ( id BIGINT PRIMARY KEY, name VARCHAR(100) )  Bảng documents documents ( id BIGINT PRIMARY KEY, title VARCHAR(255), creator_id BIGINT, status VARCHAR(20),   -- draft, processing, completed created_at TIMESTAMP )  Bảng document_logs document_logs ( id BIGINT PRIMARY KEY, document_id BIGINT, action VARCHAR(50),   -- create, approve, reject, view user_id BIGINT, created_at TIMESTAMP )     Bài test ⏱ Thời gian gợi ý: 30–40 phút 🎯 Mục tiêu: SELECT, WHERE JOIN cơ bản COUNT, GROUP BY ORDER BY Hiểu dữ liệu nghiệp vụ      Câu 1: Lấy danh sách user đang hoạt động Yêu cầu: Lấy username, full_name, email của user có status = 1      Câu 2: User theo phòng ban Yêu cầu: Lấy danh sách user kèm tên phòng ban, sắp xếp theo tên phòng ban tăng dần      Câu 3: Đếm số user theo phòng ban Yêu cầu: Mỗi phòng ban có bao nhiêu user (chỉ tính user active) Output: department_name | total_users      Câu 4: Văn bản do user tạo Yêu cầu: Lấy danh sách văn bản (title, created_at) do user có username = 'admin' tạo      Câu 5: Thống kê văn bản theo trạng thái Yêu cầu: Đếm số văn bản theo từng status     Tiêu chí pass Cơ bản Viết được SELECT đúng JOIN không sai logic Không dùng SELECT * Kết quả đúng nghiệp vụ BÀI TEST SQL – CHỦ ĐỀ: HỆ THỐNG BÁN HÀNG / ĐƠN HÀNG     I. MÔ TẢ CHUNG Giả định CSDL: MySQL / PostgreSQL     1️⃣ Bảng customers customers ( id BIGINT PRIMARY KEY, customer_code VARCHAR(50), full_name VARCHAR(100), email VARCHAR(100), status INT,           -- 1: active, 0: inactive created_at TIMESTAMP )     2️⃣ Bảng products products ( id BIGINT PRIMARY KEY, product_code VARCHAR(50), name VARCHAR(255), price DECIMAL(12,2), status INT            -- 1: selling, 0: stopped )     3️⃣ Bảng orders orders ( id BIGINT PRIMARY KEY, order_code VARCHAR(50), customer_id BIGINT, order_date TIMESTAMP, status VARCHAR(20)    -- new, paid, cancelled, completed )     4️⃣ Bảng order_items order_items ( id BIGINT PRIMARY KEY, order_id BIGINT, product_id BIGINT, quantity INT, unit_price DECIMAL(12,2) )     5️⃣ Bảng payment_logs payment_logs ( id BIGINT PRIMARY KEY, order_id BIGINT, payment_method VARCHAR(50),  -- cash, bank, momo amount DECIMAL(12,2), payment_time TIMESTAMP )     II. BÀI TEST SQL – MỨC ĐỘ CƠ BẢN ⏱ Thời gian: 30–40 phút 🎯 Mục tiêu: SELECT, WHERE JOIN cơ bản COUNT, SUM GROUP BY Hiểu nghiệp vụ bán hàng     🟢 Câu 1: Khách hàng đang hoạt động Yêu cầu: Lấy danh sách customer_code, full_name, email của khách hàng có status = 1     🟢 Câu 2: Danh sách đơn hàng kèm tên khách hàng Yêu cầu: Lấy order_code, order_date, status, full_name Sắp xếp theo order_date giảm dần     🟢 Câu 3: Thống kê số đơn theo trạng thái Yêu cầu: Đếm số lượng đơn theo từng status     🟢 Câu 4: Sản phẩm trong một đơn hàng Yêu cầu: Với order_code = 'ORD001', liệt kê: product_code | product_name | quantity | unit_price     🟢 Câu 5: Tổng tiền của mỗi đơn hàng Yêu cầu: Tính tổng tiền = SUM(quantity * unit_price) cho từng đơn hàng     ✅ Tiêu chí pass Cơ bản JOIN đúng Tính toán chính xác Không lạm dụng SELECT *     III. BÀI TEST SQL – MỨC ĐỘ NÂNG CAO ⏱ Thời gian: 60–90 phút 🎯 Mục tiêu: Subquery LEFT JOIN / NOT EXISTS Kiểm thử dữ liệu Phát hiện sai lệch nghiệp vụ SQL tối ưu     🔵 Câu 1: Khách hàng chưa từng đặt đơn Yêu cầu: Liệt kê khách hàng chưa có bất kỳ order nào     🔵 Câu 2: Đơn hàng chưa được thanh toán Yêu cầu: Lấy danh sách đơn hàng không có bản ghi trong payment_logs     🔵 Câu 3: Đơn hàng có tổng tiền khác tổng thanh toán Yêu cầu: Phát hiện các đơn hàng mà: SUM(order_items) ≠ SUM(payment_logs.amount) ➡️ Mục tiêu: kiểm thử sai lệch dữ liệu tài chính     🔵 Câu 4: Sản phẩm bán chạy nhất Yêu cầu: Tìm sản phẩm có tổng số lượng bán ra cao nhất (Chỉ tính đơn status = 'completed')     🔵 Câu 5: Lần mua gần nhất của mỗi khách hàng Yêu cầu: Với mỗi khách hàng, lấy đơn hàng mới nhất Output: customer_code | full_name | last_order_date ➡️ Gợi ý: MAX(order_date) CTE hoặc subquery     🔵 Câu 6: Phát hiện dữ liệu bất thường Yêu cầu: Liệt kê: order_items có product_id không tồn tại orders có customer_id không tồn tại ➡️ Mục tiêu: Data Integrity Testing     🔵 Câu 7 (Bonus – Tư duy hệ thống) Câu hỏi lý thuyết: Nên index những cột nào? Vì sao không nên tính tổng tiền từ orders.total_amount nếu có order_items?     ĐỀ TEST SQL – CHỦ ĐỀ: QUẢN LÝ SINH VIÊN ⏱ Thời gian làm bài: 90 phút   🎯 Mục tiêu đánh giá: ● Viết SQL chính xác ● Hiểu nghiệp vụ giáo dục ● Phát hiện dữ liệu bất thường ● Tư duy kiểm thử dữ liệu I. MÔ TẢ HỆ THỐNG & CSDL Giả định DB: MySQL / PostgreSQL 1️⃣ Bảng students students ( id BIGINT PRIMARY KEY, student_code VARCHAR(20), full_name VARCHAR(100), gender VARCHAR(10), -- male, female date_of_birth DATE, class_id BIGINT, status VARCHAR(20), -- studying, graduated, dropped created_at TIMESTAMP )   2️⃣ Bảng classes classes ( id BIGINT PRIMARY KEY, class_code VARCHAR(20), class_name VARCHAR(100), department VARCHAR(100) )   3️⃣ Bảng subjects subjects ( id BIGINT PRIMARY KEY, subject_code VARCHAR(20), subject_name VARCHAR(100), credit INT )   4️⃣ Bảng enrollments enrollments ( id BIGINT PRIMARY KEY, student_id BIGINT, subject_id BIGINT, semester VARCHAR(20), -- 2024-1, 2024-2 score DECIMAL(4,2) )   5️⃣ Bảng attendance_logs attendance_logs ( id BIGINT PRIMARY KEY, student_id BIGINT, subject_id BIGINT, attend_date DATE, status VARCHAR(20) -- present, absent )   II. PHẦN A – SQL CƠ BẢN ⏱ Thời gian gợi ý: 35–40 phút 🟢 Câu 1: Danh sách sinh viên đang học Yêu cầu:   Lấy student_code, full_name, gender, status của sinh viên có status = 'studying' 🟢 Câu 2: Sinh viên và lớp học Yêu cầu:   Lấy danh sách sinh viên kèm class_code, class_name 🟢 Câu 3: Số sinh viên theo khoa Yêu cầu:   Thống kê số lượng sinh viên theo department 🟢 Câu 4: Môn học mà sinh viên đăng ký Yêu cầu:   Với student_code = 'SV001' , liệt kê: subject_code | subject_name | semester | score   🟢 Câu 5: Điểm trung bình của mỗi sinh viên Yêu cầu:   Tính điểm trung bình (AVG) của từng sinh viên ✅ Tiêu chí pass Cơ bản ● JOIN đúng ● GROUP BY chính xác ● Không SELECT * ● SQL chạy được III. PHẦN B – SQL NÂNG CAO ⏱ Thời gian gợi ý: 50–55 phút 🔵 Câu 6: Sinh viên chưa đăng ký môn học nào Yêu cầu:   Liệt kê sinh viên không có bản ghi trong enrollments 🔵 Câu 7: Sinh viên trượt môn Yêu cầu:   Liệt kê sinh viên có bất kỳ môn nào có score < 5 🔵 Câu 8: Môn học có số sinh viên đăng ký nhiều nhất Yêu cầu:   Tìm môn học có số lượng sinh viên đăng ký cao nhất 🔵 Câu 9: Lần điểm danh gần nhất của mỗi sinh viên Yêu cầu:   Với mỗi sinh viên, lấy bản ghi điểm danh mới nhất (ngày & trạng thái) 🔵 Câu 10: Phát hiện dữ liệu bất thường Yêu cầu:   Liệt kê: enrollments có student_id không tồn tại attendance_logs có subject_id không tồn tại ➡️ Mục tiêu: Data Integrity Testing 🔵 Câu 11 (Bonus – Tư duy hệ thống) Câu hỏi ngắn: Nên index những cột nào? Vì sao không nên lưu điểm trung bình sẵn trong bảng students ?   ĐỀ TEST SQL – CHỦ ĐỀ: QUẢN LÝ Y TẾ Thời gian làm bài: 90 phút 🎯 Mục tiêu đánh giá: - Viết SQL chính xác - Hiểu nghiệp vụ y tế - Phát hiện dữ liệu bất thường - Tư duy kiểm thử dữ liệu I. MÔ TẢ HỆ THỐNG & CSDL Giả định DB: MySQL / PostgreSQL 1️⃣ Bảng patients patients ( id BIGINT PRIMARY KEY, patient_code VARCHAR(20), full_name VARCHAR(100), gender VARCHAR(10), date_of_birth DATE, phone VARCHAR(20), status VARCHAR(20), created_at TIMESTAMP ) 2️⃣ Bảng doctors doctors ( id BIGINT PRIMARY KEY, doctor_code VARCHAR(20), full_name VARCHAR(100), specialty VARCHAR(100), department VARCHAR(100), status VARCHAR(20) ) 3️⃣ Bảng appointments appointments ( id BIGINT PRIMARY KEY, patient_id BIGINT, doctor_id BIGINT, appointment_date DATE, appointment_time TIME, status VARCHAR(20) ) 4️⃣ Bảng medical_records medical_records ( id BIGINT PRIMARY KEY, patient_id BIGINT, doctor_id BIGINT, diagnosis TEXT, visit_date DATE, note TEXT ) 5️⃣ Bảng prescriptions prescriptions ( id BIGINT PRIMARY KEY, medical_record_id BIGINT, medicine_name VARCHAR(100), dosage VARCHAR(50), days INT ) II. PHẦN A – SQL CƠ BẢN ⏱ Thời gian gợi ý: 35–40 phút Câu 1: Danh sách bệnh nhân đang hoạt động Lấy patient_code, full_name, gender, status của bệnh nhân có status = 'active'. Câu 2: Lịch hẹn khám và bác sĩ Liệt kê patient_code, patient_name, doctor_name, specialty, appointment_date, status. Câu 3: Số lượng bác sĩ theo khoa Thống kê số lượng bác sĩ theo department. Câu 4: Lịch sử khám của bệnh nhân Với patient_code = 'BN001', liệt kê visit_date, doctor_name, diagnosis. Câu 5: Số lượt khám của mỗi bệnh nhân Đếm số bản ghi medical_records của từng bệnh nhân. III. PHẦN B – SQL NÂNG CAO ⏱ Thời gian gợi ý: 50–55 phút Câu 6: Bệnh nhân chưa từng đi khám Liệt kê bệnh nhân không có bản ghi trong medical_records. Câu 7: Bác sĩ có lịch hẹn bị hủy Liệt kê bác sĩ có ít nhất 1 lịch hẹn status = 'canceled'. Câu 8: Bác sĩ khám nhiều bệnh nhân nhất Tìm bác sĩ có số lượt khám nhiều nhất. Câu 9: Lần khám gần nhất của mỗi bệnh nhân Lấy patient_code, last_visit_date, doctor_name, diagnosis. Câu 10: Phát hiện dữ liệu bất thường Liệt kê appointments có patient_id không tồn tại và prescriptions có medical_record_id không tồn tại. Câu 11 (Bonus): Tư duy hệ thống 1. Nên index những cột nào? 2. Vì sao không nên lưu tổng số lượt khám trong bảng patients?