| | 1 | == Annual analysis of course enrollments, completions and certificates |
| | 2 | {{{ |
| | 3 | SELECT |
| | 4 | EXTRACT(YEAR FROM e.enroll_date) AS Year, |
| | 5 | COUNT(DISTINCT e.user_id) AS Total_Students, |
| | 6 | COUNT(DISTINCT e.enrollment_id) AS Total_Enrollments, |
| | 7 | COUNT(DISTINCT CASE WHEN e.completion_status = 'COMPLETED' THEN e.enrollment_id END) AS Completed_Enrollments, |
| | 8 | COUNT(DISTINCT cert.certificate_id) AS Total_Certificates, |
| | 9 | ROUND( |
| | 10 | COUNT(DISTINCT CASE WHEN e.completion_status = 'COMPLETED' THEN e.enrollment_id END)::DECIMAL |
| | 11 | / NULLIF(COUNT(DISTINCT e.enrollment_id), 0) * 100, 2) AS Completion_Rate_Percentage, |
| | 12 | ROUND(AVG(e.progress_percentage), 2) AS Avg_Progress_Percentage |
| | 13 | FROM enrollment e |
| | 14 | LEFT JOIN certificate cert |
| | 15 | ON cert.enrollment_id = e.enrollment_id |
| | 16 | GROUP BY EXTRACT(YEAR FROM e.enroll_date) |
| | 17 | ORDER BY Year; |
| | 18 | }}} |
| | 19 | |
| | 20 | ==== Solution Relational Algebra |
| | 21 | |
| | 22 | - E(enrollment_id, user_id, course_id, enroll_date, completion_status, progress_percentage) |
| | 23 | - CERT(certificate_id, enrollment_id, issue_date, certificate_code, status) |
| | 24 | |
| | 25 | |
| | 26 | **JOIN on all tables** |
| | 27 | |
| | 28 | J1 ← E ⟕ E.enrollment_id = CERT.enrollment_id CERT |
| | 29 | |
| | 30 | **Projection with date transformation** |
| | 31 | |
| | 32 | E' ← π enrollment_id, user_id, completion_status, progress_percentage, certificate_id, YEAR(enroll_date) → Year (J1) |
| | 33 | |
| | 34 | **Grouping and aggregate calculations** |
| | 35 | |
| | 36 | G ← Year γ |
| | 37 | COUNT(DISTINCT user_id) → Total_Students, |
| | 38 | COUNT(DISTINCT enrollment_id) → Total_Enrollments, |
| | 39 | COUNT(DISTINCT [completion_status = 'COMPLETED'] enrollment_id) → Completed_Enrollments, |
| | 40 | COUNT(DISTINCT certificate_id) → Total_Certificates, |
| | 41 | AVG(progress_percentage) → Avg_Progress_Percentage |
| | 42 | (E') |
| | 43 | |
| | 44 | **Final projection and percentage calculation** |
| | 45 | |
| | 46 | P ← π Year, Total_Students, Total_Enrollments, Completed_Enrollments, Total_Certificates, |
| | 47 | (Completed_Enrollments / Total_Enrollments) * 100 → Completion_Rate_Percentage, |
| | 48 | Avg_Progress_Percentage (G) |
| | 49 | |
| | 50 | **Chronological ordering by year** |
| | 51 | R_final ← τ Year (P) |
| | 52 | |
| | 53 | |
| | 54 | == Instructor performance analysis by number of courses, students, modules and lessons |
| | 55 | |
| | 56 | {{{ |
| | 57 | SELECT |
| | 58 | i.id AS Instructor_ID, |
| | 59 | ue.first_name || ' ' || ue.last_name AS Instructor_Name, |
| | 60 | COUNT(DISTINCT c.course_id) AS Total_Courses, |
| | 61 | COUNT(DISTINCT e.user_id) AS Total_Students, |
| | 62 | COUNT(DISTINCT m.module_id) AS Total_Modules, |
| | 63 | COUNT(DISTINCT l.lesson_id) AS Total_Lessons, |
| | 64 | ROUND(AVG(c.price), 2) AS Avg_Course_Price |
| | 65 | FROM instructors i |
| | 66 | JOIN user_entity ue |
| | 67 | ON ue.id = i.id |
| | 68 | JOIN course c |
| | 69 | ON c.instructor_id = i.id |
| | 70 | LEFT JOIN enrollment e |
| | 71 | ON e.course_id = c.course_id |
| | 72 | LEFT JOIN module m |
| | 73 | ON m.course_id = c.course_id |
| | 74 | LEFT JOIN lesson l |
| | 75 | ON l.module_id = m.module_id |
| | 76 | GROUP BY |
| | 77 | i.id, |
| | 78 | ue.first_name, |
| | 79 | ue.last_name |
| | 80 | ORDER BY |
| | 81 | Total_Students DESC, |
| | 82 | Total_Courses DESC; |
| | 83 | }}} |
| | 84 | |
| | 85 | ==== Solution Relational Algebra |
| | 86 | |
| | 87 | - I(id) |
| | 88 | - U(id, first_name, last_name, ...) |
| | 89 | - C(course_id, name, price, status, instructor_id) |
| | 90 | - E(enrollment_id, user_id, course_id, enroll_date, completion_status, progress_percentage) |
| | 91 | - M(module_id, course_id, title, description) |
| | 92 | - L(lesson_id, module_id, title, material) |
| | 93 | |
| | 94 | **JOIN on all tables:** |
| | 95 | |
| | 96 | J1 ← I ⋈ I.id = U.id U |
| | 97 | |
| | 98 | J2 ← J1 ⋈ I.id = C.instructor_id C |
| | 99 | |
| | 100 | J3 ← J2 ⋈ C.course_id = E.course_id E |
| | 101 | |
| | 102 | J4 ← J3 ⋈ C.course_id = M.course_id M |
| | 103 | |
| | 104 | J5 ← J4 ⋈ M.module_id = L.module_id L |
| | 105 | |
| | 106 | **Projection with instructor full name** |
| | 107 | |
| | 108 | F1 ← π id, CONCAT(first_name, ' ', last_name) → Instructor_Name, course_id, user_id, module_id, lesson_id, price (J5) |
| | 109 | |
| | 110 | **Grouping and aggregate calculations** |
| | 111 | |
| | 112 | G ← id, Instructor_Name γ COUNT(DISTINCT course_id) → Total_Courses, COUNT(DISTINCT user_id) → |
| | 113 | Total_Students, COUNT(DISTINCT module_id) → Total_Modules, COUNT(DISTINCT lesson_id) → |
| | 114 | Total_Lessons, AVG(price) → Avg_Course_Price (F1) |
| | 115 | |
| | 116 | **Ordering by number of students and courses** |
| | 117 | |
| | 118 | R_final ← τ Total_Students DESC, Total_Courses DESC (G) |
| | 119 | |
| | 120 | |
| | 121 | == Annual subscription and revenue analysis by subscription plan |
| | 122 | |
| | 123 | {{{ |
| | 124 | SELECT |
| | 125 | EXTRACT(YEAR FROM us.start_date) AS Year, |
| | 126 | sp.name AS Plan_Name, |
| | 127 | COUNT(DISTINCT us.subscription_id) AS Total_Subscriptions, |
| | 128 | COUNT(DISTINCT us.user_id) AS Total_Users, |
| | 129 | COUNT(DISTINCT p.payment_id) AS Total_Payments, |
| | 130 | ROUND(SUM(COALESCE(p.amount, 0)), 2) AS Total_Revenue, |
| | 131 | ROUND(AVG(COALESCE(p.amount, 0)), 2) AS Avg_Payment_Amount |
| | 132 | FROM user_subscription us |
| | 133 | JOIN subscription_plan sp |
| | 134 | ON sp.plan_id = us.plan_id |
| | 135 | LEFT JOIN payment p |
| | 136 | ON p.subscription_id = us.subscription_id |
| | 137 | GROUP BY |
| | 138 | EXTRACT(YEAR FROM us.start_date), |
| | 139 | sp.name |
| | 140 | ORDER BY |
| | 141 | Year, |
| | 142 | Total_Revenue DESC; |
| | 143 | }}} |
| | 144 | |
| | 145 | ==== Solution Relational Algebra |
| | 146 | |
| | 147 | - US(subscription_id, user_id, plan_id, start_date, end_date, status) |
| | 148 | - SP(plan_id, name, price, duration_months, description, access_type) |
| | 149 | - P(payment_id, user_id, subscription_id, amount) |
| | 150 | |
| | 151 | |
| | 152 | **JOIN on all tables** |
| | 153 | |
| | 154 | J1 ← US ⋈ US.plan_id = SP.plan_id SP |
| | 155 | |
| | 156 | J2 ← J1 ⋈ US.subscription_id = P.subscription_id P |
| | 157 | |
| | 158 | **Projection with date transformation** |
| | 159 | |
| | 160 | F1 ← π subscription_id, user_id, payment_id, amount, name, YEAR(start_date) → Year (J2) |
| | 161 | |
| | 162 | **Renaming attribute** |
| | 163 | |
| | 164 | F2 ← ρ Plan_Name/name (F1) |
| | 165 | |
| | 166 | **Grouping and aggregate calculations** |
| | 167 | |
| | 168 | G ← Year, Plan_Name γ COUNT(DISTINCT subscription_id) → Total_Subscriptions, COUNT(DISTINCT user_id) |
| | 169 | → Total_Users, COUNT(DISTINCT payment_id) → Total_Payments, SUM(amount) → Total_Revenue, |
| | 170 | AVG(amount) → Avg_Payment_Amount (F2) |
| | 171 | |
| | 172 | **Chronological ordering and descending revenue ordering** |
| | 173 | |
| | 174 | R_final ← τ Year, Total_Revenue DESC (G) |
| | 175 | |
| | 176 | |
| | 177 | == Course quiz success analysis by attempts, average score and pass rate |
| | 178 | |
| | 179 | {{{ |
| | 180 | SELECT |
| | 181 | c.course_id, |
| | 182 | c.name AS Course_Name, |
| | 183 | COUNT(DISTINCT qa.attempt_id) AS Total_Attempts, |
| | 184 | COUNT(DISTINCT qa.user_id) AS Total_Students_Attempted, |
| | 185 | ROUND(AVG(qa.score), 2) AS Avg_Score, |
| | 186 | COUNT(DISTINCT CASE WHEN qa.score >= q.passing_score THEN qa.attempt_id END) AS Passed_Attempts, |
| | 187 | ROUND( |
| | 188 | COUNT(DISTINCT CASE WHEN qa.score >= q.passing_score THEN qa.attempt_id END)::DECIMAL |
| | 189 | / NULLIF(COUNT(DISTINCT qa.attempt_id), 0) * 100, |
| | 190 | 2 |
| | 191 | ) AS Pass_Rate_Percentage |
| | 192 | FROM course c |
| | 193 | JOIN module m |
| | 194 | ON m.course_id = c.course_id |
| | 195 | JOIN lesson l |
| | 196 | ON l.module_id = m.module_id |
| | 197 | JOIN quiz q |
| | 198 | ON q.lesson_id = l.lesson_id |
| | 199 | LEFT JOIN quiz_attempt qa |
| | 200 | ON qa.quiz_id = q.quiz_id |
| | 201 | GROUP BY |
| | 202 | c.course_id, |
| | 203 | c.name |
| | 204 | ORDER BY |
| | 205 | Pass_Rate_Percentage DESC, |
| | 206 | Avg_Score DESC; |
| | 207 | |
| | 208 | ==== Solution Relational Algebra |
| | 209 | |
| | 210 | - C(course_id, name, price, status, instructor_id) |
| | 211 | - M(module_id, course_id, title, description) |
| | 212 | - L(lesson_id, module_id, title, material) |
| | 213 | - Q(quiz_id, total_points, passing_score, lesson_id) |
| | 214 | - QA(attempt_id, score, attempt_date, user_id, quiz_id) |
| | 215 | |
| | 216 | **JOIN on all tables:** |
| | 217 | |
| | 218 | J1 ← C ⋈ C.course_id = M.course_id M |
| | 219 | |
| | 220 | J2 ← J1 ⋈ M.module_id = L.module_id L |
| | 221 | |
| | 222 | J3 ← J2 ⋈ L.lesson_id = Q.lesson_id Q |
| | 223 | |
| | 224 | J4 ← J3 ⋈ Q.quiz_id = QA.quiz_id QA |
| | 225 | |
| | 226 | **Projection and renaming** |
| | 227 | |
| | 228 | F1 ← π course_id, name, attempt_id, user_id, score, passing_score (J4) |
| | 229 | |
| | 230 | F2 ← ρ Course_Name/name (F1) |
| | 231 | |
| | 232 | **Grouping and aggregate calculations** |
| | 233 | |
| | 234 | G ← course_id, Course_Name γ COUNT(DISTINCT attempt_id) → Total_Attempts, COUNT(DISTINCT user_id) |
| | 235 | → Total_Students_Attempted, AVG(score) → Avg_Score, COUNT(DISTINCT [score >= passing_score] |
| | 236 | attempt_id) → Passed_Attempts (F2) |
| | 237 | |
| | 238 | **Final projection and percentage calculation** |
| | 239 | |
| | 240 | P ← π course_id, Course_Name, Total_Attempts, Total_Students_Attempted, Avg_Score, Passed_Attempts, |
| | 241 | (Passed_Attempts / Total_Attempts) * 100 → Pass_Rate_Percentage (G) |
| | 242 | |
| | 243 | **Ordering by pass rate and average score** |
| | 244 | |
| | 245 | R_final ← τ Pass_Rate_Percentage DESC, Avg_Score DESC (P) |
| | 246 | |