| 1 | -- 1. friend_list
|
|---|
| 2 | CREATE OR REPLACE VIEW public.friend_list
|
|---|
| 3 | AS SELECT f.id AS friendship_id,
|
|---|
| 4 | u1.id AS user_id,
|
|---|
| 5 | u1.username,
|
|---|
| 6 | u2.id AS friend_id,
|
|---|
| 7 | u2.username AS friend_username,
|
|---|
| 8 | fs.name AS status
|
|---|
| 9 | FROM friendships f
|
|---|
| 10 | JOIN users u1 ON u1.id = f.user_id_high
|
|---|
| 11 | JOIN users u2 ON u2.id = f.user_id_low
|
|---|
| 12 | JOIN friendship_statuses fs ON fs.id = f.status_id
|
|---|
| 13 | WHERE fs.name = 'accepted'
|
|---|
| 14 | UNION ALL
|
|---|
| 15 | SELECT f.id AS friendship_id,
|
|---|
| 16 | u2.id AS user_id,
|
|---|
| 17 | u2.username,
|
|---|
| 18 | u1.id AS friend_id,
|
|---|
| 19 | u1.username AS friend_username,
|
|---|
| 20 | fs.name AS status
|
|---|
| 21 | FROM friendships f
|
|---|
| 22 | JOIN users u1 ON u1.id = f.user_id_high
|
|---|
| 23 | JOIN users u2 ON u2.id = f.user_id_low
|
|---|
| 24 | JOIN friendship_statuses fs ON fs.id = f.status_id
|
|---|
| 25 | WHERE fs.name = 'accepted';
|
|---|
| 26 |
|
|---|
| 27 | -- 2. game_catalog
|
|---|
| 28 | CREATE OR REPLACE VIEW public.game_catalog
|
|---|
| 29 | AS SELECT g.id AS game_id,
|
|---|
| 30 | g.title,
|
|---|
| 31 | g.release_date,
|
|---|
| 32 | string_agg(DISTINCT ge.name::text, ', '::text) AS genres,
|
|---|
| 33 | string_agg(DISTINCT d.name::text, ', '::text) AS developers,
|
|---|
| 34 | avg(r.rating) AS avg_rating,
|
|---|
| 35 | count(DISTINCT r.id) AS review_count
|
|---|
| 36 | FROM games g
|
|---|
| 37 | LEFT JOIN game_genres gg ON gg.game_id = g.id
|
|---|
| 38 | LEFT JOIN genres ge ON ge.id = gg.genre_id
|
|---|
| 39 | LEFT JOIN game_developers gd ON gd.game_id = g.id
|
|---|
| 40 | LEFT JOIN developers d ON d.id = gd.developer_id
|
|---|
| 41 | LEFT JOIN reviews r ON r.game_id = g.id
|
|---|
| 42 | GROUP BY g.id, g.title, g.release_date;
|
|---|
| 43 |
|
|---|
| 44 | -- 3. leaderboard
|
|---|
| 45 | CREATE OR REPLACE VIEW public.leaderboard
|
|---|
| 46 | AS SELECT u.id AS user_id,
|
|---|
| 47 | u.username,
|
|---|
| 48 | p.rank_points,
|
|---|
| 49 | l.country,
|
|---|
| 50 | p.avatar_url,
|
|---|
| 51 | count(DISTINCT mp.id) AS matches_played,
|
|---|
| 52 | count(DISTINCT mp.id) FILTER (WHERE pr.name = 'win') AS wins,
|
|---|
| 53 | CASE
|
|---|
| 54 | WHEN count(DISTINCT mp.id) = 0 THEN 0::numeric
|
|---|
| 55 | ELSE round(count(DISTINCT mp.id) FILTER (WHERE pr.name = 'win')::numeric * 100.0 / count(DISTINCT mp.id)::numeric, 2)
|
|---|
| 56 | END AS win_rate
|
|---|
| 57 | FROM users u
|
|---|
| 58 | JOIN profiles p ON p.user_id = u.id
|
|---|
| 59 | JOIN locations l ON l.id = u.location_id
|
|---|
| 60 | LEFT JOIN match_participants mp ON mp.user_id = u.id
|
|---|
| 61 | LEFT JOIN participant_results pr ON pr.id = mp.result_id
|
|---|
| 62 | GROUP BY u.id, u.username, p.rank_points, l.country, p.avatar_url
|
|---|
| 63 | ORDER BY p.rank_points DESC;
|
|---|
| 64 |
|
|---|
| 65 | -- 4. match_history
|
|---|
| 66 | CREATE OR REPLACE VIEW public.match_history
|
|---|
| 67 | AS SELECT m.id AS match_id,
|
|---|
| 68 | g.title AS game,
|
|---|
| 69 | gm.mode_name AS game_mode,
|
|---|
| 70 | t.name AS tournament_name,
|
|---|
| 71 | m.started_at,
|
|---|
| 72 | m.ended_at,
|
|---|
| 73 | EXTRACT(epoch FROM m.ended_at - m.started_at) / 60::numeric AS duration_minutes,
|
|---|
| 74 | l.latitude AS server_lat,
|
|---|
| 75 | l.longitude AS server_lon
|
|---|
| 76 | FROM matches m
|
|---|
| 77 | JOIN games g ON g.id = m.game_id
|
|---|
| 78 | JOIN game_modes gm ON gm.id = m.game_mode_id
|
|---|
| 79 | LEFT JOIN tournaments t ON t.id = m.tournament_id
|
|---|
| 80 | JOIN servers s ON s.id = m.server_id
|
|---|
| 81 | JOIN locations l ON l.id = s.location_id
|
|---|
| 82 | WHERE m.ended_at IS NOT NULL;
|
|---|
| 83 |
|
|---|
| 84 | -- 5. player_match_stats
|
|---|
| 85 | CREATE OR REPLACE VIEW public.player_match_stats
|
|---|
| 86 | AS SELECT mp.id AS user_match_id,
|
|---|
| 87 | u.id AS user_id,
|
|---|
| 88 | u.username,
|
|---|
| 89 | m.id AS match_id,
|
|---|
| 90 | g.title AS game,
|
|---|
| 91 | pr.name AS result,
|
|---|
| 92 | mp.score,
|
|---|
| 93 | mp.elo_change,
|
|---|
| 94 | mp.joined_at,
|
|---|
| 95 | jsonb_object_agg(st.name, ps.value) FILTER (WHERE st.id IS NOT NULL) AS stats
|
|---|
| 96 | FROM match_participants mp
|
|---|
| 97 | JOIN users u ON u.id = mp.user_id
|
|---|
| 98 | JOIN matches m ON m.id = mp.match_id AND m.started_at = mp.match_started_at
|
|---|
| 99 | JOIN games g ON g.id = m.game_id
|
|---|
| 100 | LEFT JOIN participant_results pr ON pr.id = mp.result_id
|
|---|
| 101 | LEFT JOIN participant_stats ps ON ps.match_participant_id = mp.id
|
|---|
| 102 | LEFT JOIN stat_types st ON st.id = ps.stat_type_id
|
|---|
| 103 | GROUP BY mp.id, u.id, u.username, m.id, g.title, pr.name, mp.score, mp.elo_change, mp.joined_at;
|
|---|
| 104 |
|
|---|
| 105 | -- 6. tournament_standings
|
|---|
| 106 | CREATE OR REPLACE VIEW public.tournament_standings
|
|---|
| 107 | AS SELECT t.id AS tournament_id,
|
|---|
| 108 | t.name AS tournament_name,
|
|---|
| 109 | g.title AS game,
|
|---|
| 110 | t.starts_at,
|
|---|
| 111 | t.prize_pool,
|
|---|
| 112 | tm.id AS team_id,
|
|---|
| 113 | tm.name AS team_name,
|
|---|
| 114 | count(DISTINCT m.id) AS matches_played,
|
|---|
| 115 | count(DISTINCT m.id) FILTER (WHERE m.id IS NOT NULL) AS matches_entered
|
|---|
| 116 | FROM tournaments t
|
|---|
| 117 | JOIN games g ON g.id = t.game_id
|
|---|
| 118 | JOIN tournament_teams tt ON tt.tournament_id = t.id
|
|---|
| 119 | JOIN teams tm ON tm.id = tt.team_id
|
|---|
| 120 | LEFT JOIN matches m ON m.tournament_id = t.id
|
|---|
| 121 | GROUP BY t.id, t.name, g.title, t.starts_at, t.prize_pool, tm.id, tm.name
|
|---|
| 122 | ORDER BY t.id, (count(DISTINCT m.id)) DESC;
|
|---|
| 123 |
|
|---|
| 124 | -- 7. unread_notifications
|
|---|
| 125 | CREATE OR REPLACE VIEW public.unread_notifications
|
|---|
| 126 | AS SELECT n.id AS notification_id,
|
|---|
| 127 | u.id AS user_id,
|
|---|
| 128 | u.username,
|
|---|
| 129 | nt.name AS type,
|
|---|
| 130 | n.message,
|
|---|
| 131 | n.is_read,
|
|---|
| 132 | g.title AS preferred_game
|
|---|
| 133 | FROM notifications n
|
|---|
| 134 | JOIN users u ON u.id = n.user_id
|
|---|
| 135 | JOIN notification_types nt ON nt.id = n.type_id
|
|---|
| 136 | LEFT JOIN preferences pr ON pr.user_id = n.preference_user_id AND pr.game_id = n.preference_game_id
|
|---|
| 137 | LEFT JOIN games g ON g.id = pr.game_id
|
|---|
| 138 | WHERE n.is_read = false
|
|---|
| 139 | ORDER BY n.id DESC;
|
|---|
| 140 |
|
|---|
| 141 | -- 8. user_all_achievements
|
|---|
| 142 | CREATE OR REPLACE VIEW public.user_all_achievements
|
|---|
| 143 | AS SELECT u.id AS user_id,
|
|---|
| 144 | u.username,
|
|---|
| 145 | a.id AS achievement_id,
|
|---|
| 146 | a.title AS achievement_title,
|
|---|
| 147 | a.description AS achievement_description,
|
|---|
| 148 | g.title AS game,
|
|---|
| 149 | ua.unlocked_at
|
|---|
| 150 | FROM user_achievements ua
|
|---|
| 151 | JOIN users u ON u.id = ua.user_id
|
|---|
| 152 | JOIN achievements a ON a.id = ua.achievement_id
|
|---|
| 153 | JOIN games g ON g.id = a.game_id
|
|---|
| 154 | ORDER BY ua.unlocked_at DESC;
|
|---|
| 155 |
|
|---|
| 156 | -- 9. user_profile
|
|---|
| 157 | CREATE OR REPLACE VIEW public.user_profile
|
|---|
| 158 | AS SELECT u.id AS user_id,
|
|---|
| 159 | u.username,
|
|---|
| 160 | u.email,
|
|---|
| 161 | l.latitude,
|
|---|
| 162 | l.longitude,
|
|---|
| 163 | u.created_at,
|
|---|
| 164 | p.avatar_url,
|
|---|
| 165 | p.bio,
|
|---|
| 166 | l.country,
|
|---|
| 167 | p.rank_points,
|
|---|
| 168 | sp.name AS subscription_type,
|
|---|
| 169 | s.start_at AS subscription_start,
|
|---|
| 170 | s.end_at AS subscription_end
|
|---|
| 171 | FROM users u
|
|---|
| 172 | JOIN profiles p ON p.user_id = u.id
|
|---|
| 173 | JOIN locations l ON l.id = u.location_id
|
|---|
| 174 | LEFT JOIN subscriptions s ON s.user_id = u.id AND (s.end_at IS NULL OR s.end_at >= CURRENT_DATE) AND s.start_at <= CURRENT_DATE
|
|---|
| 175 | LEFT JOIN subscription_plans sp ON sp.id = s.plan_id; |
|---|