| | 278 | |
| | 279 | == 4# Yearly analysis of overall platform activity and student success across enrollments, quiz attempts, completions, certificates, and course performance |
| | 280 | |
| | 281 | This SQL query provides a comprehensive yearly analysis of the platform by combining multiple aspects of user activity, instructor engagement, and student performance. |
| | 282 | Active platform performance is evaluated through: |
| | 283 | - Instructor activity based on courses with enrollments. |
| | 284 | - Student participation through enrollments and quiz attempts |
| | 285 | - Course completion status and issued certificates |
| | 286 | - Student success is measured through progress percentage |
| | 287 | - Quiz performance based on average scores and pass rate |
| | 288 | - Course-level success calculated as average quiz score per course |
| | 289 | - Overall yearly success calculated as the average performance across all courses |
| | 290 | This query: |
| | 291 | - Aggregates data year by year to observe platform growth and trends |
| | 292 | - Combines multiple entities, including instructors, students, courses, enrollments, quizzes, and certificates |
| | 293 | - Calculates both activity-based and performance-based metrics |
| | 294 | - Derives course-level and overall yearly success indicators |
| | 295 | - Provides a multi-dimensional analytical view of the platform |
| | 296 | |
| | 297 | {{{ |
| | 298 | SELECT |
| | 299 | EXTRACT(YEAR FROM qa.attempt_date) AS Year, |
| | 300 | cat.name AS Category_Name, |
| | 301 | COUNT(DISTINCT qa.attempt_id) AS Total_Attempts, |
| | 302 | COUNT(DISTINCT qa.user_id) AS Total_Students, |
| | 303 | ROUND(AVG(qa.score), 2) AS Avg_Score, |
| | 304 | COUNT( |
| | 305 | DISTINCT CASE |
| | 306 | WHEN qa.score >= q.passing_score |
| | 307 | THEN qa.attempt_id |
| | 308 | END |
| | 309 | ) AS Passed_Attempts, |
| | 310 | ROUND( |
| | 311 | COUNT( |
| | 312 | DISTINCT CASE |
| | 313 | WHEN qa.score >= q.passing_score |
| | 314 | THEN qa.attempt_id |
| | 315 | END |
| | 316 | )::DECIMAL / |
| | 317 | NULLIF(COUNT(DISTINCT qa.attempt_id),0) * 100, |
| | 318 | 2 |
| | 319 | ) AS Pass_Rate_Percentage |
| | 320 | FROM quiz_attempt qa |
| | 321 | JOIN quiz q |
| | 322 | ON q.quiz_id = qa.quiz_id |
| | 323 | JOIN lesson l |
| | 324 | ON l.lesson_id = q.lesson_id |
| | 325 | JOIN module m |
| | 326 | ON m.module_id = l.module_id |
| | 327 | JOIN course c |
| | 328 | ON c.course_id = m.course_id |
| | 329 | JOIN course_category cc |
| | 330 | ON cc.course_id = c.course_id |
| | 331 | JOIN category cat |
| | 332 | ON cat.category_id = cc.category_id |
| | 333 | GROUP BY |
| | 334 | EXTRACT(YEAR FROM qa.attempt_date), |
| | 335 | cat.name |
| | 336 | ORDER BY |
| | 337 | Year, |
| | 338 | Pass_Rate_Percentage DESC; |
| | 339 | |
| | 340 | }}} |
| | 341 | |
| | 342 | |
| | 343 | ==== Solution Relational Algebra |
| | 344 | |
| | 345 | - QA(attempt_id, score, attempt_date, user_id, quiz_id) |
| | 346 | - Q(quiz_id, total_points, passing_score, lesson_id) |
| | 347 | - L(lesson_id, module_id, title, material) |
| | 348 | - M(module_id, course_id, title, description) |
| | 349 | - C(course_id, name, price, status, instructor_id) |
| | 350 | - CC(course_id, category_id) |
| | 351 | - CAT(category_id, name, description) |
| | 352 | |
| | 353 | |
| | 354 | **JOIN on all tables:** |
| | 355 | |
| | 356 | J1 ← QA ⋈ QA.quiz_id = Q.quiz_id Q |
| | 357 | |
| | 358 | J2 ← J1 ⋈ Q.lesson_id = L.lesson_id L |
| | 359 | |
| | 360 | J3 ← J2 ⋈ L.module_id = M.module_id M |
| | 361 | |
| | 362 | J4 ← J3 ⋈ M.course_id = C.course_id C |
| | 363 | |
| | 364 | J5 ← J4 ⋈ C.course_id = CC.course_id CC |
| | 365 | |
| | 366 | J6 ← J5 ⋈ CC.category_id = CAT.category_id CAT |
| | 367 | |
| | 368 | **Projection and renaming** |
| | 369 | |
| | 370 | F1 ← π attempt_id, user_id, score, passing_score, CAT.name → Category_Name, YEAR(attempt_date) → Year (J6) |
| | 371 | |
| | 372 | |
| | 373 | **Grouping and aggregate calculations** |
| | 374 | |
| | 375 | G ← Year, Category_Name γ |
| | 376 | |
| | 377 | COUNT(DISTINCT attempt_id) → Total_Attempts, |
| | 378 | |
| | 379 | COUNT(DISTINCT user_id) → Total_Students, |
| | 380 | |
| | 381 | AVG(score) → Avg_Score, |
| | 382 | |
| | 383 | COUNT(DISTINCT [score ≥ passing_score] attempt_id) → Passed_Attempts (F1) |
| | 384 | |
| | 385 | **Final projection and percentage calculation** |
| | 386 | |
| | 387 | P ← π Year, Category_Name, Total_Attempts, Total_Students, Avg_Score, Passed_Attempts, |
| | 388 | |
| | 389 | (Passed_Attempts / Total_Attempts) * 100 → Pass_Rate_Percentage (G) |
| | 390 | |
| | 391 | |
| | 392 | **Chronological ordering by year** |
| | 393 | |
| | 394 | R_final ← τ Year, Pass_Rate_Percentage DESC (P) |