| | 13 | {{ |
| | 14 | EXPLAIN ANALYZE |
| | 15 | WITH genre_annual AS ( |
| | 16 | SELECT |
| | 17 | DATE_TRUNC('year', s.story_created_at) AS year, |
| | 18 | g.genre_id, |
| | 19 | g.genre_name, |
| | 20 | COUNT(DISTINCT s.story_id) AS total_stories, |
| | 21 | COUNT(DISTINCT w.user_id) AS total_writers, |
| | 22 | COALESCE(SUM(ch.view_count), 0) AS total_views, |
| | 23 | COALESCE(SUM(ch.word_count), 0) AS total_words, |
| | 24 | COUNT(DISTINCT l.user_id) AS total_likes, |
| | 25 | COUNT(DISTINCT c.comment_id) AS total_comments, |
| | 26 | ROUND(AVG(ch.rating), 2) AS avg_rating |
| | 27 | FROM genre g |
| | 28 | JOIN has_genre hg ON g.genre_id = hg.genre_id |
| | 29 | JOIN story s ON hg.story_id = s.story_id |
| | 30 | JOIN writer w ON s.user_id = w.user_id |
| | 31 | JOIN status st ON s.story_id = st.story_id AND st.status = 'published' |
| | 32 | LEFT JOIN chapter ch ON s.story_id = ch.story_id |
| | 33 | LEFT JOIN likes l ON s.story_id = l.story_id |
| | 34 | LEFT JOIN comment c ON s.story_id = c.story_id |
| | 35 | GROUP BY DATE_TRUNC('year', s.story_created_at), g.genre_id, g.genre_name |
| | 36 | ), |
| | 37 | with_metrics AS ( |
| | 38 | SELECT *, |
| | 39 | ROUND((total_likes + total_comments)::DECIMAL / NULLIF(total_views, 0) * 100, 2) AS engagement_rate, |
| | 40 | ROUND(total_views::DECIMAL / NULLIF(total_stories, 0), 2) AS avg_views_per_story, |
| | 41 | LAG(total_views) OVER (PARTITION BY genre_id ORDER BY year) AS prev_year_views, |
| | 42 | LAG(total_stories) OVER (PARTITION BY genre_id ORDER BY year) AS prev_year_stories |
| | 43 | FROM genre_annual |
| | 44 | ) |
| | 45 | SELECT |
| | 46 | TO_CHAR(year, 'YYYY') AS year, |
| | 47 | genre_name, |
| | 48 | total_stories, total_writers, total_views, avg_views_per_story, |
| | 49 | total_likes, total_comments, |
| | 50 | COALESCE(avg_rating, 0) AS avg_rating, |
| | 51 | COALESCE(engagement_rate, 0) AS engagement_rate, |
| | 52 | ROUND((total_views - prev_year_views)::DECIMAL / NULLIF(prev_year_views, 0) * 100, 2) AS yoy_views_growth_pct, |
| | 53 | ROUND((total_stories - prev_year_stories)::DECIMAL / NULLIF(prev_year_stories, 0) * 100, 2) AS yoy_stories_growth_pct, |
| | 54 | RANK() OVER (PARTITION BY year ORDER BY total_views DESC) AS popularity_rank, |
| | 55 | RANK() OVER (PARTITION BY year ORDER BY engagement_rate DESC) AS engagement_rank |
| | 56 | FROM with_metrics |
| | 57 | ORDER BY year DESC, popularity_rank; |
| | 58 | }} |