Conversation
There was a problem hiding this comment.
Pull request overview
This pull request adds homework solutions for two SQL lab assignments (TP1 and TP2) covering database management systems. The submissions demonstrate comprehensive SQL skills including database design, data manipulation, and complex query construction.
Changes:
- Added tp1_solutions.sql with a complete University Management System implementation including schema design, test data, and 30 practice queries
- Added tp2_solutions.sql with a complete Hospital Management System implementation including schema design, test data, and 30 practice queries
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| submissions/SS/Benzina-mohamed/tp1_solutions.sql | University Management System with departments, professors, students, courses, enrollments, and grades tables plus 30 SQL queries demonstrating various SQL operations |
| submissions/SS/Benzina-mohamed/tp2_solutions.sql | Hospital Management System with specialties, doctors, patients, consultations, medications, prescriptions tables plus 30 SQL queries demonstrating various SQL operations |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| GROUP BY d.doctor_id; | ||
|
|
||
| -- Q27. Display top 3 most profitable specialties | ||
| SELECT RANK() OVER (ORDER BY SUM(c.amount) DESC) as `rank`, s.specialty_name, SUM(c.amount) AS total_revenue |
There was a problem hiding this comment.
The query uses backticks around 'rank' which is appropriate since RANK is a reserved keyword in SQL. However, for consistency and clarity, consider using a non-reserved word like 'ranking' or 'position' to avoid the need for escaping.
| SELECT RANK() OVER (ORDER BY SUM(c.amount) DESC) as `rank`, s.specialty_name, SUM(c.amount) AS total_revenue | |
| SELECT RANK() OVER (ORDER BY SUM(c.amount) DESC) AS position, s.specialty_name, SUM(c.amount) AS total_revenue |
| 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; |
There was a problem hiding this comment.
The GROUP BY clause groups by p.patient_id but also selects d.last_name and d.first_name. When a patient has multiple consultations with different doctors, MySQL's ONLY_FULL_GROUP_BY mode will reject this query. The doctor information shown will be arbitrary from one of the consultations, not necessarily from the most recent consultation.
| 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; | |
| SELECT CONCAT(p.last_name, ' ', p.first_name) AS patient_name, | |
| lc.last_consultation_date, | |
| CONCAT(d.last_name, ' ', d.first_name) AS doctor_name | |
| FROM patients p | |
| LEFT JOIN ( | |
| SELECT patient_id, MAX(consultation_date) AS last_consultation_date | |
| FROM consultations | |
| GROUP BY patient_id | |
| ) AS lc ON p.patient_id = lc.patient_id | |
| LEFT JOIN consultations c | |
| ON c.patient_id = lc.patient_id | |
| AND c.consultation_date = lc.last_consultation_date | |
| LEFT JOIN doctors d ON c.doctor_id = d.doctor_id; |
| SELECT CONCAT(p.last_name, ' ', p.first_name) AS patient_name, | ||
| COUNT(c.consultation_id) AS consultation_count, | ||
| (SELECT AVG(cnt) FROM (SELECT COUNT(consultation_id) as cnt FROM consultations GROUP BY patient_id) as sub) as average_count | ||
| FROM patients p | ||
| JOIN consultations c ON p.patient_id = c.patient_id | ||
| GROUP BY p.patient_id | ||
| HAVING consultation_count > (SELECT AVG(cnt) FROM (SELECT COUNT(consultation_id) as cnt FROM consultations GROUP BY patient_id) as sub); |
There was a problem hiding this comment.
The subquery calculating the average is executed twice - once in the SELECT and once in the HAVING clause. This is inefficient and could impact performance on larger datasets. Consider using a single calculation by referencing it through a WITH clause or calculate it once and store in a variable.
| SELECT CONCAT(p.last_name, ' ', p.first_name) AS patient_name, | |
| COUNT(c.consultation_id) AS consultation_count, | |
| (SELECT AVG(cnt) FROM (SELECT COUNT(consultation_id) as cnt FROM consultations GROUP BY patient_id) as sub) as average_count | |
| FROM patients p | |
| JOIN consultations c ON p.patient_id = c.patient_id | |
| GROUP BY p.patient_id | |
| HAVING consultation_count > (SELECT AVG(cnt) FROM (SELECT COUNT(consultation_id) as cnt FROM consultations GROUP BY patient_id) as sub); | |
| WITH avg_consultations AS ( | |
| SELECT AVG(cnt) AS average_count | |
| FROM ( | |
| SELECT COUNT(consultation_id) AS cnt | |
| FROM consultations | |
| GROUP BY patient_id | |
| ) AS sub | |
| ) | |
| SELECT CONCAT(p.last_name, ' ', p.first_name) AS patient_name, | |
| COUNT(c.consultation_id) AS consultation_count, | |
| ac.average_count AS average_count | |
| FROM patients p | |
| JOIN consultations c ON p.patient_id = c.patient_id | |
| CROSS JOIN avg_consultations ac | |
| GROUP BY p.patient_id, ac.average_count | |
| HAVING consultation_count > ac.average_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 | ||
| HAVING enrollment_count > ( | ||
| SELECT AVG(cnt) FROM (SELECT COUNT(enrollment_id) as cnt FROM enrollments GROUP BY course_id) as sub | ||
| ); |
There was a problem hiding this comment.
The subquery calculating the average enrollment count is executed in the HAVING clause. For better performance and readability, consider using a WITH clause to calculate this value once and reference it in the HAVING clause.
| 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 | |
| HAVING enrollment_count > ( | |
| SELECT AVG(cnt) FROM (SELECT COUNT(enrollment_id) as cnt FROM enrollments GROUP BY course_id) as sub | |
| ); | |
| WITH CourseEnrollmentCounts AS ( | |
| SELECT e.course_id, COUNT(e.enrollment_id) AS enrollment_count | |
| FROM enrollments e | |
| GROUP BY e.course_id | |
| ), AvgEnrollment AS ( | |
| SELECT AVG(enrollment_count) AS avg_enrollment_count | |
| FROM CourseEnrollmentCounts | |
| ) | |
| SELECT c.course_name, cec.enrollment_count | |
| FROM courses c | |
| JOIN CourseEnrollmentCounts cec ON c.course_id = cec.course_id | |
| CROSS JOIN AvgEnrollment ae | |
| WHERE cec.enrollment_count > ae.avg_enrollment_count; |
| SELECT d.department_name, COUNT(s.student_id) AS student_count | ||
| FROM departments d | ||
| JOIN students s ON d.department_id = s.department_id | ||
| GROUP BY d.department_id | ||
| HAVING student_count > ( | ||
| SELECT AVG(cnt) FROM (SELECT COUNT(student_id) as cnt FROM students GROUP BY department_id) as sub | ||
| ); |
There was a problem hiding this comment.
The subquery calculating the average student count is executed in the HAVING clause. For better performance and readability, consider using a WITH clause to calculate this value once and reference it in the HAVING clause.
| SELECT d.department_name, COUNT(s.student_id) AS student_count | |
| FROM departments d | |
| JOIN students s ON d.department_id = s.department_id | |
| GROUP BY d.department_id | |
| HAVING student_count > ( | |
| SELECT AVG(cnt) FROM (SELECT COUNT(student_id) as cnt FROM students GROUP BY department_id) as sub | |
| ); | |
| WITH avg_student_count AS ( | |
| SELECT AVG(cnt) AS avg_cnt | |
| FROM ( | |
| SELECT COUNT(student_id) AS cnt | |
| FROM students | |
| GROUP BY department_id | |
| ) AS sub | |
| ) | |
| SELECT d.department_name, COUNT(s.student_id) AS student_count | |
| FROM departments d | |
| JOIN students s ON d.department_id = s.department_id | |
| CROSS JOIN avg_student_count | |
| GROUP BY d.department_id | |
| HAVING student_count > avg_student_count.avg_cnt; |
| GROUP BY c.course_id; | ||
|
|
||
| -- Q27. Display student ranking by descending average | ||
| SELECT RANK() OVER (ORDER BY AVG(g.grade) DESC) as `rank`, |
There was a problem hiding this comment.
The query uses backticks around 'rank' which is appropriate since RANK is a reserved keyword in SQL. However, for consistency and clarity, consider using a non-reserved word like 'ranking' or 'position' to avoid the need for escaping.
| SELECT RANK() OVER (ORDER BY AVG(g.grade) DESC) as `rank`, | |
| SELECT RANK() OVER (ORDER BY AVG(g.grade) DESC) AS ranking, |
| JOIN enrollments e ON s.student_id = e.student_id | ||
| WHERE s.student_id NOT IN (SELECT student_id FROM enrollments WHERE status != 'Passed') |
There was a problem hiding this comment.
This query has a logical issue. It's supposed to display students who have passed ALL their courses, but the NOT IN subquery excludes students who have ANY non-Passed status. This means students with a mix of 'Passed' and 'In Progress' courses will still appear in the results. The query should ensure that ALL of a student's enrollments have status = 'Passed', not just that they have at least one passed course.
| JOIN enrollments e ON s.student_id = e.student_id | |
| WHERE s.student_id NOT IN (SELECT student_id FROM enrollments WHERE status != 'Passed') | |
| JOIN enrollments e ON s.student_id = e.student_id AND e.status = 'Passed' | |
| WHERE NOT EXISTS ( | |
| SELECT 1 | |
| FROM enrollments e2 | |
| WHERE e2.student_id = s.student_id | |
| AND e2.status <> 'Passed' | |
| ) |
| 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; |
There was a problem hiding this comment.
The LEFT JOIN with doctors in this query may produce incorrect results. When there are multiple consultations for a patient with different doctors, the MAX(c.consultation_date) might be grouped with a doctor from a different consultation. Consider using a subquery or window function to correctly identify which doctor was seen at the last consultation date for each patient.
| 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; | |
| c_last.consultation_date AS last_consultation_date, | |
| CONCAT(d.last_name, ' ', d.first_name) AS doctor_name | |
| FROM patients p | |
| LEFT JOIN consultations c_last | |
| ON c_last.consultation_id = ( | |
| SELECT c2.consultation_id | |
| FROM consultations c2 | |
| WHERE c2.patient_id = p.patient_id | |
| ORDER BY c2.consultation_date DESC, c2.consultation_id DESC | |
| LIMIT 1 | |
| ) | |
| LEFT JOIN doctors d ON c_last.doctor_id = d.doctor_id; |
| SELECT commercial_name, unit_price, (SELECT AVG(unit_price) FROM medications) as average_price | ||
| FROM medications | ||
| WHERE unit_price > (SELECT AVG(unit_price) FROM medications); |
There was a problem hiding this comment.
The subquery calculating the average unit price is executed twice - once in the SELECT and once in the WHERE clause. This is inefficient and could impact performance on larger datasets. Consider calculating it once and storing in a variable or using a WITH clause.
| SELECT commercial_name, unit_price, (SELECT AVG(unit_price) FROM medications) as average_price | |
| FROM medications | |
| WHERE unit_price > (SELECT AVG(unit_price) FROM medications); | |
| SELECT m.commercial_name, | |
| m.unit_price, | |
| avg_med.average_price | |
| FROM medications m | |
| JOIN (SELECT AVG(unit_price) AS average_price FROM medications) AS avg_med | |
| WHERE m.unit_price > avg_med.average_price; |
| SELECT c.consultation_date, CONCAT(p.last_name, ' ', p.first_name) AS patient_name, c.amount, | ||
| (SELECT AVG(amount) FROM consultations) as average_amount | ||
| FROM consultations c | ||
| JOIN patients p ON c.patient_id = p.patient_id | ||
| WHERE c.amount > (SELECT AVG(amount) FROM consultations); |
There was a problem hiding this comment.
The subquery calculating the average consultation amount is executed twice - once in the SELECT and once in the WHERE clause. This is inefficient and could impact performance on larger datasets. Consider calculating it once and storing in a variable or using a WITH clause.
| SELECT c.consultation_date, CONCAT(p.last_name, ' ', p.first_name) AS patient_name, c.amount, | |
| (SELECT AVG(amount) FROM consultations) as average_amount | |
| FROM consultations c | |
| JOIN patients p ON c.patient_id = p.patient_id | |
| WHERE c.amount > (SELECT AVG(amount) FROM consultations); | |
| SELECT c.consultation_date, | |
| CONCAT(p.last_name, ' ', p.first_name) AS patient_name, | |
| c.amount, | |
| avg_stats.average_amount | |
| FROM consultations c | |
| JOIN patients p ON c.patient_id = p.patient_id | |
| JOIN ( | |
| SELECT AVG(amount) AS average_amount | |
| FROM consultations | |
| ) AS avg_stats | |
| WHERE c.amount > avg_stats.average_amount; |
No description provided.