diff --git a/submissions/AI/ouinten_mohammed_amine/tp1_solution.sql b/submissions/AI/ouinten_mohammed_amine/tp1_solution.sql new file mode 100644 index 0000000..1662ce7 --- /dev/null +++ b/submissions/AI/ouinten_mohammed_amine/tp1_solution.sql @@ -0,0 +1,562 @@ +CREATE DATABASE university_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE university_db; + + +-- TABLE CREATION ================================ + + +-- Departments +CREATE TABLE departments ( + department_id INT AUTO_INCREMENT PRIMARY KEY, + department_name VARCHAR(100) NOT NULL, + building VARCHAR(50), + budget DECIMAL(12, 2), + department_head VARCHAR(100), + creation_date DATE +); + +-- Professors +CREATE TABLE professors ( + professor_id INT AUTO_INCREMENT PRIMARY KEY, + last_name VARCHAR(50) NOT NULL, + first_name VARCHAR(50) NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + phone VARCHAR(20), + department_id INT, + hire_date DATE, + salary DECIMAL(10, 2), + specialization VARCHAR(100), + FOREIGN KEY (department_id) REFERENCES departments(department_id) + ON DELETE SET NULL + ON UPDATE CASCADE +); + +-- Students +CREATE TABLE students ( + student_id INT AUTO_INCREMENT PRIMARY KEY, + student_number VARCHAR(20) UNIQUE NOT NULL, + last_name VARCHAR(50) NOT NULL, + first_name VARCHAR(50) NOT NULL, + date_of_birth DATE, + email VARCHAR(100) UNIQUE NOT NULL, + phone VARCHAR(20), + address TEXT, + department_id INT, + level VARCHAR(20) CHECK (level IN ('L1', 'L2', 'L3', 'M1', 'M2')), + enrollment_date DATE DEFAULT (CURRENT_DATE), + FOREIGN KEY (department_id) REFERENCES departments(department_id) + ON DELETE SET NULL + ON UPDATE CASCADE +); + +-- Courses +CREATE TABLE courses ( + course_id INT AUTO_INCREMENT PRIMARY KEY, + course_code VARCHAR(10) UNIQUE NOT NULL, + course_name VARCHAR(150) NOT NULL, + description TEXT, + credits INT NOT NULL CHECK (credits > 0), + semester INT CHECK (semester BETWEEN 1 AND 2), + department_id INT, + professor_id INT, + max_capacity INT DEFAULT 30, + FOREIGN KEY (department_id) REFERENCES departments(department_id) + ON DELETE CASCADE + ON UPDATE CASCADE, + FOREIGN KEY (professor_id) REFERENCES professors(professor_id) + ON DELETE SET NULL + ON UPDATE CASCADE +); + +-- Enrollments +CREATE TABLE enrollments ( + enrollment_id INT AUTO_INCREMENT PRIMARY KEY, + student_id INT NOT NULL, + course_id INT NOT NULL, + enrollment_date DATE DEFAULT (CURRENT_DATE), + academic_year VARCHAR(9) NOT NULL, + status VARCHAR(20) DEFAULT 'In Progress' + CHECK (status IN ('In Progress', 'Passed', 'Failed', 'Dropped')), + FOREIGN KEY (student_id) REFERENCES students(student_id) + ON DELETE CASCADE + ON UPDATE CASCADE, + FOREIGN KEY (course_id) REFERENCES courses(course_id) + ON DELETE CASCADE + ON UPDATE CASCADE, + UNIQUE KEY unique_enrollment (student_id, course_id, academic_year) +); + +-- Grades +CREATE TABLE grades ( + grade_id INT AUTO_INCREMENT PRIMARY KEY, + enrollment_id INT NOT NULL, + evaluation_type VARCHAR(30) + CHECK (evaluation_type IN ('Assignment', 'Lab', 'Exam', 'Project')), + grade DECIMAL(5, 2) CHECK (grade BETWEEN 0 AND 20), + coefficient DECIMAL(3, 2) DEFAULT 1.00, + evaluation_date DATE, + comments TEXT, + FOREIGN KEY (enrollment_id) REFERENCES enrollments(enrollment_id) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +-- Indexes + +CREATE INDEX idx_student_department ON students(department_id); +CREATE INDEX idx_course_professor ON courses(professor_id); +CREATE INDEX idx_enrollment_student ON enrollments(student_id); +CREATE INDEX idx_enrollment_course ON enrollments(course_id); +CREATE INDEX idx_grades_enrollment ON grades(enrollment_id); + + +-- Insert Data ======================================================= + + +-- Insert Departments +INSERT INTO departments (department_name, building, budget, department_head, creation_date) VALUES +('Computer Science', 'North Wing', 520000.00, 'Dr. Amira Bensalem', '2011-08-15'), +('Mathematics', 'East Wing', 360000.00, 'Prof. Karim Benali', '2009-09-01'), +('Physics', 'West Wing', 410000.00, 'Dr. Nadia Khelifi', '2013-03-12'), +('Civil Engineering', 'Central Building', 600000.00, 'Prof. Youssef Touati', '2007-06-20'); + +-- Insert Professors +INSERT INTO professors (last_name, first_name, email, phone, department_id, hire_date, salary, specialization) VALUES +('Belkacem', 'Sami', 'sami.belkacem@university.edu', '+213-555-1011', 1, '2017-09-01', 75000.00, 'Machine Learning'), +('Brahimi', 'Leila', 'leila.brahimi@university.edu', '+213-555-1022', 1, '2018-01-15', 72000.00, 'Database Systems'), +('Cherif', 'Karim', 'karim.cherif@university.edu', '+213-555-1033', 1, '2019-09-01', 68000.00, 'Web Development'), +('Amrani', 'Fatiha', 'fatiha.amrani@university.edu', '+213-555-1044', 2, '2016-09-01', 70000.00, 'Statistics'), +('Haddad', 'Rachid', 'rachid.haddad@university.edu', '+213-555-1055', 3, '2015-09-01', 73000.00, 'Quantum Mechanics'), +('Kacem', 'Nadia', 'nadia.kacem@university.edu', '+213-555-1066', 4, '2014-09-01', 78000.00, 'Structural Engineering'); +-- Insert Students +INSERT INTO students (student_number, last_name, first_name, date_of_birth, email, phone, address, department_id, level, enrollment_date) VALUES +('CS2021001', 'Saidi', 'Rania', '2003-06-12', 'rania.saidi@student.edu', '+213-661-0101', '12 Rue Ali, Algiers', 1, 'L3', '2021-09-01'), +('CS2022002', 'Moussa', 'Omar', '2004-03-08', 'omar.moussa@student.edu', '+213-661-0102', '8 Avenue Didouche, Algiers', 1, 'L2', '2022-09-01'), +('MA2021003', 'Fares', 'Sara', '2003-11-21', 'sara.fares@student.edu', '+213-661-0103', '24 Rue Abane, Algiers', 2, 'L3', '2021-09-01'), +('PH2022004', 'Benali', 'Youssef', '2004-01-19', 'youssef.benali@student.edu', '+213-661-0104', '5 Boulevard Amirouche, Algiers', 3, 'L2', '2022-09-01'), +('CS2020005', 'Khaldi', 'Lina', '2002-07-05', 'lina.khaldi@student.edu', '+213-661-0105', '18 Rue Didouche, Algiers', 1, 'M1', '2020-09-01'), +('CE2021006', 'Toumi', 'Hassan', '2003-09-12', 'hassan.toumi@student.edu', '+213-661-0106', '33 Avenue Larbi, Algiers', 4, 'L3', '2021-09-01'), +('CS2022007', 'Ait Ahmed', 'Mila', '2004-02-14', 'mila.aitahmed@student.edu', '+213-661-0107', '40 Rue Didouche, Algiers', 1, 'L2', '2022-09-01'), +('MA2020008', 'Zerrouki', 'Samir', '2002-10-30', 'samir.zerrouki@student.edu', '+213-661-0108', '10 Boulevard Amirouche, Algiers', 2, 'M1', '2020-09-01'); + + +-- Insert Courses +INSERT INTO courses (course_code, course_name, description, credits, semester, department_id, professor_id, max_capacity) VALUES +('CS301', 'Database Systems', 'Advanced database design and SQL programming', 6, 1, 1, 2, 35), +('CS302', 'Artificial Intelligence', 'Intro to AI and machine learning', 6, 1, 1, 1, 30), +('CS401', 'Web Development', 'Full-stack web development', 5, 2, 1, 3, 32), +('MA201', 'Linear Algebra', 'Vectors and matrices', 5, 1, 2, 4, 40), +('PH301', 'Quantum Mechanics', 'Basics of quantum physics', 6, 1, 3, 5, 25), +('CE301', 'Structural Analysis', 'Study of building structures', 6, 2, 4, 6, 28), +('CS303', 'Data Structures', 'Algorithms and data structures', 5, 2, 1, 1, 35); + +-- Insert Enrollments +INSERT INTO enrollments (student_id, course_id, enrollment_date, academic_year, status) VALUES +(1, 1, '2024-09-15', '2024-2025', 'In Progress'), +(1, 2, '2024-09-15', '2024-2025', 'In Progress'), +(1, 3, '2024-09-15', '2024-2025', 'In Progress'), +(2, 1, '2024-09-15', '2024-2025', 'In Progress'), +(2, 7, '2024-09-15', '2024-2025', 'In Progress'), +(5, 1, '2024-09-15', '2024-2025', 'Passed'), +(5, 2, '2024-09-15', '2024-2025', 'Passed'), +(5, 3, '2024-09-15', '2024-2025', 'In Progress'), +(7, 1, '2024-09-15', '2024-2025', 'In Progress'), +(7, 7, '2024-09-15', '2024-2025', 'In Progress'), +(3, 4, '2023-09-10', '2023-2024', 'Passed'), +(4, 5, '2023-09-10', '2023-2024', 'Passed'), +(6, 6, '2023-09-10', '2023-2024', 'Passed'), +(8, 4, '2023-09-10', '2023-2024', 'Passed'), +(1, 7, '2023-09-10', '2023-2024', 'Failed'); + +-- Insert Grades +INSERT INTO grades (enrollment_id, evaluation_type, grade, coefficient, evaluation_date, comments) VALUES +(1, 'Assignment', 15.50, 0.20, '2024-10-15', 'Good understanding of normalization'), +(1, 'Exam', 14.00, 0.60, '2024-12-10', 'Solid performance'), +(2, 'Lab', 16.00, 0.30, '2024-11-05', 'Excellent implementation'), +(2, 'Project', 17.50, 0.50, '2024-12-15', 'Outstanding project work'), +(6, 'Assignment', 18.00, 0.20, '2024-10-15', 'Excellent work'), +(6, 'Exam', 16.50, 0.60, '2024-12-10', 'Very good understanding'), +(7, 'Lab', 17.00, 0.30, '2024-11-05', 'Great implementation'), +(7, 'Exam', 15.50, 0.50, '2024-12-18', 'Good performance'), +(11, 'Exam', 14.50, 1.00, '2024-01-20', 'Passed'), +(12, 'Exam', 13.00, 1.00, '2024-01-22', 'Passed'), +(13, 'Project', 16.00, 1.00, '2024-05-15', 'Excellent project'), +(14, 'Exam', 12.50, 1.00, '2024-01-20', 'Passed'); + + + +-- Q1. List all students with their main information (name, email, level) +-- Expected columns: last_name, first_name, email, level +SELECT + last_name, + first_name, + email, + level +FROM students +ORDER BY last_name, first_name; + +-- Q2. Display all professors from the Computer Science department +-- Expected columns: last_name, first_name, email, specialization +SELECT + p.last_name, + p.first_name, + p.email, + p.specialization +FROM professors p +JOIN departments d ON p.department_id = d.department_id +WHERE d.department_name = 'Computer Science' +ORDER BY p.last_name, p.first_name; + +-- Q3. Find all courses with more than 5 credits +-- Expected columns: course_code, course_name, credits +SELECT + course_code, + course_name, + credits +FROM courses +WHERE credits > 5 +ORDER BY credits DESC; + +-- Q4. List students enrolled in L3 level +-- Expected columns: student_number, last_name, first_name, email +SELECT + student_number, + last_name, + first_name, + email +FROM students +WHERE level = 'L3' +ORDER BY last_name, first_name; + +-- Q5. Display courses from semester 1 +-- Expected columns: course_code, course_name, credits, semester +SELECT + course_code, + course_name, + credits, + semester +FROM courses +WHERE semester = 1 +ORDER BY course_code; + + +-- Q6. Display all courses with the professor's name +-- Expected columns: course_code, course_name, professor_name (last + first) +SELECT + c.course_code, + c.course_name, + CONCAT(p.last_name, ' ', p.first_name) AS professor_name +FROM courses c +LEFT JOIN professors p ON c.professor_id = p.professor_id +ORDER BY c.course_code; + +-- Q7. List all enrollments with student name and course name +-- Expected columns: student_name, course_name, enrollment_date, status +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + c.course_name, + e.enrollment_date, + e.status +FROM enrollments e +JOIN students s ON e.student_id = s.student_id +JOIN courses c ON e.course_id = c.course_id +ORDER BY e.enrollment_date DESC; + +-- Q8. Display students with their department name +-- Expected columns: student_name, department_name, level +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + d.department_name, + s.level +FROM students s +LEFT JOIN departments d ON s.department_id = d.department_id +ORDER BY d.department_name, s.level; + +-- Q9. List grades with student name, course name, and grade obtained +-- Expected columns: student_name, course_name, evaluation_type, grade +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + c.course_name, + g.evaluation_type, + g.grade +FROM grades g +JOIN enrollments e ON g.enrollment_id = e.enrollment_id +JOIN students s ON e.student_id = s.student_id +JOIN courses c ON e.course_id = c.course_id +ORDER BY s.last_name, c.course_name; + +-- Q10. Display professors with the number of courses they teach +-- Expected columns: professor_name, number_of_courses +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS professor_name, + COUNT(c.course_id) AS number_of_courses +FROM professors p +LEFT JOIN courses c ON p.professor_id = c.professor_id +GROUP BY p.professor_id, p.last_name, p.first_name +ORDER BY number_of_courses DESC; + + + +-- Q11. Calculate the overall average grade for each student +-- Expected columns: student_name, average_grade +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + ROUND(AVG(g.grade), 2) AS average_grade +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +JOIN grades g ON e.enrollment_id = g.enrollment_id +GROUP BY s.student_id, s.last_name, s.first_name +ORDER BY average_grade DESC; + +-- Q12. Count the number of students per department +-- Expected columns: department_name, student_count +SELECT + d.department_name, + COUNT(s.student_id) AS student_count +FROM departments d +LEFT JOIN students s ON d.department_id = s.department_id +GROUP BY d.department_id, d.department_name +ORDER BY student_count DESC; + +-- Q13. Calculate the total budget of all departments +-- Expected result: One row with total_budget +SELECT + SUM(budget) AS total_budget +FROM departments; + +-- Q14. Find the total number of courses per department +-- Expected columns: department_name, course_count +SELECT + d.department_name, + COUNT(c.course_id) AS course_count +FROM departments d +LEFT JOIN courses c ON d.department_id = c.department_id +GROUP BY d.department_id, d.department_name +ORDER BY course_count DESC; + +-- Q15. Calculate the average salary of professors per department +-- Expected columns: department_name, average_salary +SELECT + d.department_name, + ROUND(AVG(p.salary), 2) AS average_salary +FROM departments d +LEFT JOIN professors p ON d.department_id = p.department_id +GROUP BY d.department_id, d.department_name +ORDER BY average_salary DESC; + + +-- Q16. Find the top 3 students with the best averages +-- Expected columns: student_name, average_grade +-- Order by average_grade DESC, limit 3 +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + ROUND(AVG(g.grade), 2) AS average_grade +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +JOIN grades g ON e.enrollment_id = g.enrollment_id +GROUP BY s.student_id, s.last_name, s.first_name +ORDER BY average_grade DESC +LIMIT 3; + +-- Q17. List courses with no enrolled students +-- Expected columns: course_code, course_name +SELECT + c.course_code, + c.course_name +FROM courses c +LEFT JOIN enrollments e ON c.course_id = e.course_id +WHERE e.enrollment_id IS NULL +ORDER BY c.course_code; + +-- Q18. Display students who have passed all their courses (status = 'Passed') +-- Expected columns: student_name, passed_courses_count +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + COUNT(e.enrollment_id) AS passed_courses_count +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +WHERE e.status = 'Passed' +GROUP BY s.student_id, s.last_name, s.first_name +HAVING COUNT(e.enrollment_id) = ( + SELECT COUNT(*) + FROM enrollments e2 + WHERE e2.student_id = s.student_id +) +ORDER BY passed_courses_count DESC; + +-- Q19. Find professors who teach more than 2 courses +-- Expected columns: professor_name, courses_taught +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS professor_name, + COUNT(c.course_id) AS courses_taught +FROM professors p +JOIN courses c ON p.professor_id = c.professor_id +GROUP BY p.professor_id, p.last_name, p.first_name +HAVING COUNT(c.course_id) > 2 +ORDER BY courses_taught DESC; + +-- Q20. List students enrolled in more than 2 courses +-- Expected columns: student_name, enrolled_courses_count +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + COUNT(e.enrollment_id) AS enrolled_courses_count +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +GROUP BY s.student_id, s.last_name, s.first_name +HAVING COUNT(e.enrollment_id) > 2 +ORDER BY enrolled_courses_count DESC; + + + +-- Q21. Find students with an average higher than their department's average +-- Expected columns: student_name, student_avg, department_avg +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + ROUND(AVG(g.grade), 2) AS student_avg, + ROUND(( + SELECT AVG(g2.grade) + FROM grades g2 + JOIN enrollments e2 ON g2.enrollment_id = e2.enrollment_id + JOIN students s2 ON e2.student_id = s2.student_id + WHERE s2.department_id = s.department_id + ), 2) AS department_avg +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +JOIN grades g ON e.enrollment_id = g.enrollment_id +GROUP BY s.student_id, s.last_name, s.first_name, s.department_id +HAVING student_avg > department_avg; + +-- Q22. List courses with more enrollments than the average number of enrollments +-- Expected columns: course_name, enrollment_count +SELECT + c.course_name, + COUNT(e.enrollment_id) AS enrollment_count +FROM courses c +JOIN enrollments e ON c.course_id = e.course_id +GROUP BY c.course_id, c.course_name +HAVING COUNT(e.enrollment_id) > ( + SELECT AVG(enrollment_count) + FROM ( + SELECT COUNT(enrollment_id) AS enrollment_count + FROM enrollments + GROUP BY course_id + ) AS avg_enrollments +) +ORDER BY enrollment_count DESC; + +-- Q23. Display professors from the department with the highest budget +-- Expected columns: professor_name, department_name, budget +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS professor_name, + d.department_name, + d.budget +FROM professors p +JOIN departments d ON p.department_id = d.department_id +WHERE d.budget = ( + SELECT MAX(budget) + FROM departments +) +ORDER BY p.last_name, p.first_name; + +-- Q24. Find students with no grades recorded +-- Expected columns: student_name, email +SELECT + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + s.email +FROM students s +WHERE NOT EXISTS ( + SELECT 1 + FROM enrollments e + JOIN grades g ON e.enrollment_id = g.enrollment_id + WHERE e.student_id = s.student_id +) +ORDER BY s.last_name, s.first_name; + +-- Q25. List departments with more students than the average +-- Expected columns: department_name, student_count +SELECT + d.department_name, + COUNT(s.student_id) AS student_count +FROM departments d +LEFT JOIN students s ON d.department_id = s.department_id +GROUP BY d.department_id, d.department_name +HAVING COUNT(s.student_id) > ( + SELECT AVG(student_count) + FROM ( + SELECT COUNT(student_id) AS student_count + FROM students + GROUP BY department_id + ) AS avg_students +) +ORDER BY student_count DESC; + + + +-- Q26. Calculate the pass rate per course (grades >= 10/20) +-- Expected columns: course_name, total_grades, passed_grades, pass_rate_percentage +SELECT + c.course_name, + COUNT(g.grade_id) AS total_grades, + SUM(CASE WHEN g.grade >= 10 THEN 1 ELSE 0 END) AS passed_grades, + ROUND( + (SUM(CASE WHEN g.grade >= 10 THEN 1 ELSE 0 END) * 100.0) / COUNT(g.grade_id), + 2 + ) AS pass_rate_percentage +FROM courses c +JOIN enrollments e ON c.course_id = e.course_id +JOIN grades g ON e.enrollment_id = g.enrollment_id +GROUP BY c.course_id, c.course_name +ORDER BY pass_rate_percentage DESC; + +-- Q27. Display student ranking by descending average +-- Expected columns: rank, student_name, average_grade +SELECT + RANK() OVER (ORDER BY AVG(g.grade) DESC) AS `rank`, + CONCAT(s.last_name, ' ', s.first_name) AS student_name, + ROUND(AVG(g.grade), 2) AS average_grade +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +JOIN grades g ON e.enrollment_id = g.enrollment_id +GROUP BY s.student_id, s.last_name, s.first_name +ORDER BY average_grade DESC; + +-- Q28. Generate a report card for student with student_id = 1 +-- Expected columns: course_name, evaluation_type, grade, coefficient, weighted_grade +SELECT + c.course_name, + g.evaluation_type, + g.grade, + g.coefficient, + ROUND(g.grade * g.coefficient, 2) AS weighted_grade +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +JOIN courses c ON e.course_id = c.course_id +JOIN grades g ON e.enrollment_id = g.enrollment_id +WHERE s.student_id = 1 +ORDER BY c.course_name, g.evaluation_date; + +-- Q29. Calculate teaching load per professor (total credits taught) +-- Expected columns: professor_name, total_credits +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS professor_name, + COALESCE(SUM(c.credits), 0) AS total_credits +FROM professors p +LEFT JOIN courses c ON p.professor_id = c.professor_id +GROUP BY p.professor_id, p.last_name, p.first_name +ORDER BY total_credits DESC; + +-- Q30. Identify overloaded courses (enrollments > 80% of max capacity) +-- Expected columns: course_name, current_enrollments, max_capacity, percentage_full +SELECT + c.course_name, + COUNT(e.enrollment_id) AS current_enrollments, + c.max_capacity, + ROUND( + (COUNT(e.enrollment_id) * 100.0) / c.max_capacity, + 2 + ) AS percentage_full +FROM courses c +LEFT JOIN enrollments e ON c.course_id = e.course_id +GROUP BY c.course_id, c.course_name, c.max_capacity +HAVING percentage_full > 80 +ORDER BY percentage_full DESC; diff --git a/submissions/AI/ouinten_mohammed_amine/tp2_solution.sql b/submissions/AI/ouinten_mohammed_amine/tp2_solution.sql new file mode 100644 index 0000000..dee716d --- /dev/null +++ b/submissions/AI/ouinten_mohammed_amine/tp2_solution.sql @@ -0,0 +1,402 @@ +-- Q1. List all patients with their main information +-- Expected: file_number, full_name, date_of_birth, phone, city +SELECT + file_number, + CONCAT(last_name, ' ', first_name) AS full_name, + date_of_birth, + phone, + city +FROM patients +ORDER BY last_name, first_name; + +-- Q2. Display all doctors with their specialty +-- Expected: doctor_name, specialty_name, office, active +SELECT + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + s.specialty_name, + d.office, + CASE WHEN d.active = 1 THEN 'Yes' ELSE 'No' END AS active +FROM doctors d +JOIN specialties s ON d.specialty_id = s.specialty_id +ORDER BY s.specialty_name, d.last_name; + +-- Q3. Find all medications with price less than 500 DA +-- Expected: medication_code, commercial_name, unit_price, available_stock +SELECT + medication_code, + commercial_name, + unit_price, + available_stock +FROM medications +WHERE unit_price < 500 +ORDER BY unit_price; + +-- Q4. List consultations from January 2025 +-- Expected: consultation_date, patient_name, doctor_name, status +SELECT + c.consultation_date, + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + c.status +FROM consultations c +JOIN patients p ON c.patient_id = p.patient_id +JOIN doctors d ON c.doctor_id = d.doctor_id +WHERE YEAR(c.consultation_date) = 2025 AND MONTH(c.consultation_date) = 1 +ORDER BY c.consultation_date; + +-- Q5. Display medications where stock is below minimum stock +-- Expected: commercial_name, available_stock, minimum_stock, difference +SELECT + commercial_name, + available_stock, + minimum_stock, + (minimum_stock - available_stock) AS difference +FROM medications +WHERE available_stock < minimum_stock +ORDER BY difference DESC; + + +-- Q6. Display all consultations with patient and doctor names +-- Expected: consultation_date, patient_name, doctor_name, diagnosis, amount +SELECT + c.consultation_date, + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + c.diagnosis, + c.amount +FROM consultations c +JOIN patients p ON c.patient_id = p.patient_id +JOIN doctors d ON c.doctor_id = d.doctor_id +WHERE c.status = 'Completed' +ORDER BY c.consultation_date DESC; + +-- Q7. List all prescriptions with medication details +-- Expected: prescription_date, patient_name, medication_name, quantity, dosage_instructions +SELECT + pr.prescription_date, + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + m.commercial_name AS medication_name, + pd.quantity, + pd.dosage_instructions +FROM prescriptions pr +JOIN consultation c ON pr.consultation_id = c.consultation_id +JOIN patients p ON c.patient_id = p.patient_id +JOIN prescription_details pd ON pr.prescription_id = pd.prescription_id +JOIN medications m ON pd.medication_id = m.medication_id +ORDER BY pr.prescription_date DESC, p.last_name; + +-- Q8. Display patients with their last consultation date +-- Expected: patient_name, last_consultation_date, doctor_name +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + MAX(c.consultation_date) AS last_consultation_date, + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name +FROM patients p +LEFT JOIN consultations c ON p.patient_id = c.patient_id +LEFT JOIN doctors d ON c.doctor_id = d.doctor_id +GROUP BY p.patient_id, p.last_name, p.first_name, d.last_name, d.first_name +ORDER BY last_consultation_date DESC; + +-- Q9. List doctors and the number of consultations performed +-- Expected: doctor_name, consultation_count +SELECT + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + COUNT(c.consultation_id) AS consultation_count +FROM doctors d +LEFT JOIN consultations c ON d.doctor_id = c.doctor_id +WHERE c.status = 'Completed' +GROUP BY d.doctor_id, d.last_name, d.first_name +ORDER BY consultation_count DESC; + +-- Q10. Display revenue by medical specialty +-- Expected: specialty_name, total_revenue, consultation_count +SELECT + s.specialty_name, + COALESCE(SUM(c.amount), 0) AS total_revenue, + COUNT(c.consultation_id) AS consultation_count +FROM specialties s +LEFT JOIN doctors d ON s.specialty_id = d.specialty_id +LEFT JOIN consultations c ON d.doctor_id = c.doctor_id AND c.paid = 1 +GROUP BY s.specialty_id, s.specialty_name +ORDER BY total_revenue DESC; + + + +-- Q11. Calculate total prescription amount per patient +-- Expected: patient_name, total_prescription_cost +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + COALESCE(SUM(pd.total_price), 0) AS total_prescription_cost +FROM patients p +LEFT JOIN consultations c ON p.patient_id = c.patient_id +LEFT JOIN prescriptions pr ON c.consultation_id = pr.consultation_id +LEFT JOIN prescription_details pd ON pr.prescription_id = pd.prescription_id +GROUP BY p.patient_id, p.last_name, p.first_name +ORDER BY total_prescription_cost DESC; + +-- Q12. Count the number of consultations per doctor +-- Expected: doctor_name, consultation_count +SELECT + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + COUNT(c.consultation_id) AS consultation_count +FROM doctors d +LEFT JOIN consultations c ON d.doctor_id = c.doctor_id +GROUP BY d.doctor_id, d.last_name, d.first_name +ORDER BY consultation_count DESC; + +-- Q13. Calculate total stock value of pharmacy +-- Expected: total_medications, total_stock_value +SELECT + COUNT(medication_id) AS total_medications, + SUM(unit_price * available_stock) AS total_stock_value +FROM medications; + +-- Q14. Find average consultation price per specialty +-- Expected: specialty_name, average_price +SELECT + s.specialty_name, + ROUND(AVG(c.amount), 2) AS average_price +FROM specialties s +JOIN doctors d ON s.specialty_id = d.specialty_id +JOIN consultations c ON d.doctor_id = c.doctor_id +WHERE c.status = 'Completed' +GROUP BY s.specialty_id, s.specialty_name +ORDER BY average_price DESC; + +-- Q15. Count number of patients by blood type +-- Expected: blood_type, patient_count +SELECT + COALESCE(blood_type, 'Not Specified') AS blood_type, + COUNT(patient_id) AS patient_count +FROM patients +GROUP BY blood_type +ORDER BY patient_count DESC; + + +-- Q16. Find the top 5 most prescribed medications +-- Expected: medication_name, times_prescribed, total_quantity +SELECT + m.commercial_name AS medication_name, + COUNT(DISTINCT pd.prescription_id) AS times_prescribed, + SUM(pd.quantity) AS total_quantity +FROM medications m +JOIN prescription_details pd ON m.medication_id = pd.medication_id +GROUP BY m.medication_id, m.commercial_name +ORDER BY times_prescribed DESC, total_quantity DESC +LIMIT 5; + +-- Q17. List patients who have never had a consultation +-- Expected: patient_name, registration_date +SELECT + CONCAT(last_name, ' ', first_name) AS patient_name, + registration_date +FROM patients p +WHERE NOT EXISTS ( + SELECT 1 FROM consultations c WHERE c.patient_id = p.patient_id +) +ORDER BY registration_date; + +-- Q18. Display doctors who performed more than 2 consultations +-- Expected: doctor_name, specialty, consultation_count +SELECT + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + s.specialty_name AS specialty, + COUNT(c.consultation_id) AS consultation_count +FROM doctors d +JOIN specialties s ON d.specialty_id = s.specialty_id +JOIN consultations c ON d.doctor_id = c.doctor_id +WHERE c.status = 'Completed' +GROUP BY d.doctor_id, d.last_name, d.first_name, s.specialty_name +HAVING COUNT(c.consultation_id) > 2 +ORDER BY consultation_count DESC; + +-- Q19. Find unpaid consultations with total amount +-- Expected: patient_name, consultation_date, amount, doctor_name +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + c.consultation_date, + c.amount, + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name +FROM consultations c +JOIN patients p ON c.patient_id = p.patient_id +JOIN doctors d ON c.doctor_id = d.doctor_id +WHERE c.paid = 0 AND c.status = 'Completed' +ORDER BY c.consultation_date; + +-- Q20. List medications expiring in less than 6 months from today +-- Expected: medication_name, expiration_date, days_until_expiration +SELECT + commercial_name AS medication_name, + expiration_date, + DATEDIFF(expiration_date, CURDATE()) AS days_until_expiration +FROM medications +WHERE expiration_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 6 MONTH) +ORDER BY days_until_expiration; + + +-- Q21. Find patients who consulted more than the average +-- Expected: patient_name, consultation_count, average_count +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + COUNT(c.consultation_id) AS consultation_count, + ROUND(( + SELECT AVG(consultation_count) + FROM ( + SELECT COUNT(consultation_id) AS consultation_count + FROM consultations + GROUP BY patient_id + ) AS avg_consultations + ), 2) AS average_count +FROM patients p +JOIN consultations c ON p.patient_id = c.patient_id +GROUP BY p.patient_id, p.last_name, p.first_name +HAVING consultation_count > ( + SELECT AVG(consultation_count) + FROM ( + SELECT COUNT(consultation_id) AS consultation_count + FROM consultations + GROUP BY patient_id + ) AS avg_consultations +) +ORDER BY consultation_count DESC; + +-- Q22. List medications more expensive than average price +-- Expected: medication_name, unit_price, average_price +SELECT + commercial_name AS medication_name, + unit_price, + ROUND(( + SELECT AVG(unit_price) + FROM medications + ), 2) AS average_price +FROM medications +WHERE unit_price > (SELECT AVG(unit_price) FROM medications) +ORDER BY unit_price DESC; + +-- Q23. Display doctors from the most requested specialty +-- Expected: doctor_name, specialty_name, specialty_consultation_count +SELECT + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + s.specialty_name, + COUNT(c.consultation_id) AS specialty_consultation_count +FROM doctors d +JOIN specialties s ON d.specialty_id = s.specialty_id +JOIN consultations c ON d.doctor_id = c.doctor_id +WHERE s.specialty_id = ( + SELECT d2.specialty_id + FROM doctors d2 + JOIN consultations c2 ON d2.doctor_id = c2.doctor_id + GROUP BY d2.specialty_id + ORDER BY COUNT(c2.consultation_id) DESC + LIMIT 1 +) +GROUP BY d.doctor_id, d.last_name, d.first_name, s.specialty_name +ORDER BY specialty_consultation_count DESC; + +-- Q24. Find consultations with amount higher than average +-- Expected: consultation_date, patient_name, amount, average_amount +SELECT + c.consultation_date, + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + c.amount, + ROUND(( + SELECT AVG(amount) + FROM consultations + WHERE status = 'Completed' + ), 2) AS average_amount +FROM consultations c +JOIN patients p ON c.patient_id = p.patient_id +WHERE c.amount > ( + SELECT AVG(amount) + FROM consultations + WHERE status = 'Completed' +) +ORDER BY c.amount DESC; + +-- Q25. List allergic patients who received a prescription +-- Expected: patient_name, allergies, prescription_count +SELECT + CONCAT(p.last_name, ' ', p.first_name) AS patient_name, + p.allergies, + COUNT(pr.prescription_id) AS prescription_count +FROM patients p +JOIN consultations c ON p.patient_id = c.patient_id +JOIN prescriptions pr ON c.consultation_id = pr.consultation_id +WHERE p.allergies IS NOT NULL AND p.allergies != '' +GROUP BY p.patient_id, p.last_name, p.first_name, p.allergies +ORDER BY prescription_count DESC; + + + +-- Q26. Calculate total revenue per doctor (paid consultations only) +-- Expected: doctor_name, total_consultations, total_revenue +SELECT + CONCAT(d.last_name, ' ', d.first_name) AS doctor_name, + COUNT(c.consultation_id) AS total_consultations, + COALESCE(SUM(c.amount), 0) AS total_revenue +FROM doctors d +LEFT JOIN consultations c ON d.doctor_id = c.doctor_id AND c.paid = 1 +GROUP BY d.doctor_id, d.last_name, d.first_name +ORDER BY total_revenue DESC; + +-- Q27. Display top 3 most profitable specialties +-- Expected: rank, specialty_name, total_revenue +SELECT + RANK() OVER (ORDER BY COALESCE(SUM(c.amount), 0) DESC) AS `rank`, + s.specialty_name, + COALESCE(SUM(c.amount), 0) AS total_revenue +FROM specialties s +LEFT JOIN doctors d ON s.specialty_id = d.specialty_id +LEFT JOIN consultations c ON d.doctor_id = c.doctor_id AND c.paid = 1 +GROUP BY s.specialty_id, s.specialty_name +ORDER BY total_revenue DESC +LIMIT 3; + +-- Q28. List medications to restock (stock < minimum) +-- Expected: medication_name, current_stock, minimum_stock, quantity_needed +SELECT + commercial_name AS medication_name, + available_stock AS current_stock, + minimum_stock, + (minimum_stock - available_stock) AS quantity_needed +FROM medications +WHERE available_stock < minimum_stock +ORDER BY quantity_needed DESC; + +-- Q29. Calculate average number of medications per prescription +-- Expected: average_medications_per_prescription +SELECT + ROUND(AVG(medication_count), 2) AS average_medications_per_prescription +FROM ( + SELECT + pr.prescription_id, + COUNT(pd.medication_id) AS medication_count + FROM prescriptions pr + LEFT JOIN prescription_details pd ON pr.prescription_id = pd.prescription_id + GROUP BY pr.prescription_id +) AS prescription_med_counts; + +-- Q30. Generate patient demographics report by age group +-- Age groups: 0-18, 19-40, 41-60, 60+ +-- Expected: age_group, patient_count, percentage +SELECT + CASE + WHEN TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) <= 18 THEN '0-18' + WHEN TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) BETWEEN 19 AND 40 THEN '19-40' + WHEN TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) BETWEEN 41 AND 60 THEN '41-60' + ELSE '60+' + END AS age_group, + COUNT(patient_id) AS patient_count, + ROUND( + (COUNT(patient_id) * 100.0) / (SELECT COUNT(*) FROM patients), + 2 + ) AS percentage +FROM patients +GROUP BY age_group +ORDER BY + CASE age_group + WHEN '0-18' THEN 1 + WHEN '19-40' THEN 2 + WHEN '41-60' THEN 3 + WHEN '60+' THEN 4 + END;