| 1 | -- 1. Event sales summary
|
|---|
| 2 | -- Used by organisers/admin dashboard.
|
|---|
| 3 | CREATE OR REPLACE VIEW public.v_event_sales_summary AS
|
|---|
| 4 | SELECT
|
|---|
| 5 | e.event_id,
|
|---|
| 6 | e.title AS event_title,
|
|---|
| 7 | o.organiser_id,
|
|---|
| 8 | o.company_name AS organiser_name,
|
|---|
| 9 | COUNT(t.ticket_id) AS tickets_sold,
|
|---|
| 10 | COUNT(DISTINCT oc.order_id) AS total_orders,
|
|---|
| 11 | COALESCE(SUM(p.amount_paid), 0) AS total_revenue,
|
|---|
| 12 | ROUND(AVG(p.amount_paid), 2) AS average_payment_amount,
|
|---|
| 13 | MIN(oc.created_at) AS first_order_at,
|
|---|
| 14 | MAX(oc.created_at) AS last_order_at
|
|---|
| 15 | FROM public.event e
|
|---|
| 16 | JOIN public.organiser o
|
|---|
| 17 | ON o.organiser_id = e.organiser_id
|
|---|
| 18 | JOIN public.ticket_type tt
|
|---|
| 19 | ON tt.event_id = e.event_id
|
|---|
| 20 | JOIN public.ticket t
|
|---|
| 21 | ON t.ticket_type_id = tt.ticket_type_id
|
|---|
| 22 | JOIN public.order_cart oc
|
|---|
| 23 | ON oc.order_id = t.order_id
|
|---|
| 24 | LEFT JOIN public.payment p
|
|---|
| 25 | ON p.order_id = oc.order_id
|
|---|
| 26 | GROUP BY
|
|---|
| 27 | e.event_id,
|
|---|
| 28 | e.title,
|
|---|
| 29 | o.organiser_id,
|
|---|
| 30 | o.company_name;
|
|---|
| 31 |
|
|---|
| 32 | select *
|
|---|
| 33 | from v_event_sales_summary;
|
|---|
| 34 |
|
|---|
| 35 | -- 2. User purchase profile
|
|---|
| 36 | -- Used for user account page / CRM / recommendations.
|
|---|
| 37 | CREATE OR REPLACE VIEW public.v_user_purchase_profile AS
|
|---|
| 38 | SELECT
|
|---|
| 39 | u.user_id,
|
|---|
| 40 | u.username,
|
|---|
| 41 | u.email,
|
|---|
| 42 | COUNT(DISTINCT oc.order_id) AS total_orders,
|
|---|
| 43 | COUNT(t.ticket_id) AS total_tickets,
|
|---|
| 44 | COALESCE(SUM(p.amount_paid), 0) AS total_spent,
|
|---|
| 45 | ROUND(AVG(p.amount_paid), 2) AS average_order_payment,
|
|---|
| 46 | MIN(oc.created_at) AS first_purchase_at,
|
|---|
| 47 | MAX(oc.created_at) AS last_purchase_at
|
|---|
| 48 | FROM public.user_app u
|
|---|
| 49 | LEFT JOIN public.order_cart oc
|
|---|
| 50 | ON oc.user_id = u.user_id
|
|---|
| 51 | LEFT JOIN public.ticket t
|
|---|
| 52 | ON t.order_id = oc.order_id
|
|---|
| 53 | LEFT JOIN public.payment p
|
|---|
| 54 | ON p.order_id = oc.order_id
|
|---|
| 55 | GROUP BY
|
|---|
| 56 | u.user_id,
|
|---|
| 57 | u.username,
|
|---|
| 58 | u.email;
|
|---|
| 59 |
|
|---|
| 60 | select *
|
|---|
| 61 | from v_user_purchase_profile;
|
|---|
| 62 |
|
|---|
| 63 | -- 3. Ticket scan statistics per event
|
|---|
| 64 | -- Used by event check-in dashboard.
|
|---|
| 65 | CREATE OR REPLACE VIEW public.v_event_checkin_statistics AS
|
|---|
| 66 | SELECT
|
|---|
| 67 | e.event_id,
|
|---|
| 68 | e.title AS event_title,
|
|---|
| 69 | COUNT(t.ticket_id) AS total_tickets,
|
|---|
| 70 | COUNT(*) FILTER (WHERE t.is_scanned = true) AS scanned_tickets,
|
|---|
| 71 | COUNT(*) FILTER (WHERE t.is_scanned = false) AS unscanned_tickets,
|
|---|
| 72 | ROUND(
|
|---|
| 73 | 100.0 * COUNT(*) FILTER (WHERE t.is_scanned = true)
|
|---|
| 74 | / NULLIF(COUNT(t.ticket_id), 0),
|
|---|
| 75 | 2
|
|---|
| 76 | ) AS scanned_percentage,
|
|---|
| 77 | MIN(t.scanned_at) FILTER (WHERE t.is_scanned = true) AS first_scan_at,
|
|---|
| 78 | MAX(t.scanned_at) FILTER (WHERE t.is_scanned = true) AS last_scan_at
|
|---|
| 79 | FROM public.event e
|
|---|
| 80 | JOIN public.ticket_type tt
|
|---|
| 81 | ON tt.event_id = e.event_id
|
|---|
| 82 | JOIN public.ticket t
|
|---|
| 83 | ON t.ticket_type_id = tt.ticket_type_id
|
|---|
| 84 | GROUP BY
|
|---|
| 85 | e.event_id,
|
|---|
| 86 | e.title;
|
|---|
| 87 |
|
|---|
| 88 | select *
|
|---|
| 89 | from v_event_checkin_statistics;
|
|---|
| 90 |
|
|---|
| 91 | -- 4. Session occupancy and waitlist pressure
|
|---|
| 92 | -- Used to see overloaded sessions.
|
|---|
| 93 | CREATE OR REPLACE VIEW public.v_session_demand_overview AS
|
|---|
| 94 | SELECT
|
|---|
| 95 | ess.schedule_id,
|
|---|
| 96 | ess.session_title,
|
|---|
| 97 | e.event_id,
|
|---|
| 98 | e.title AS event_title,
|
|---|
| 99 | l.name AS location_name,
|
|---|
| 100 | s.section_name,
|
|---|
| 101 | s.capacity AS section_capacity,
|
|---|
| 102 | COUNT(DISTINCT w.waitlist_id) AS waitlist_entries,
|
|---|
| 103 | COUNT(DISTINCT w.waitlist_id) FILTER (WHERE w.status = 'WAITING') AS active_waitlist_entries,
|
|---|
| 104 | ROUND(
|
|---|
| 105 | COUNT(DISTINCT w.waitlist_id)::numeric
|
|---|
| 106 | / NULLIF(s.capacity, 0),
|
|---|
| 107 | 4
|
|---|
| 108 | ) AS waitlist_to_capacity_ratio
|
|---|
| 109 | FROM public.event_schedule_session ess
|
|---|
| 110 | JOIN public.event e
|
|---|
| 111 | ON e.event_id = ess.event_id
|
|---|
| 112 | JOIN public.section s
|
|---|
| 113 | ON s.section_id = ess.section_id
|
|---|
| 114 | JOIN public.location l
|
|---|
| 115 | ON l.location_id = s.location_id
|
|---|
| 116 | LEFT JOIN public.waitlist_entry w
|
|---|
| 117 | ON w.event_schedule_session_id = ess.schedule_id
|
|---|
| 118 | GROUP BY
|
|---|
| 119 | ess.schedule_id,
|
|---|
| 120 | ess.session_title,
|
|---|
| 121 | e.event_id,
|
|---|
| 122 | e.title,
|
|---|
| 123 | l.name,
|
|---|
| 124 | s.section_name,
|
|---|
| 125 | s.capacity;
|
|---|
| 126 |
|
|---|
| 127 | select *
|
|---|
| 128 | from v_session_demand_overview;
|
|---|
| 129 |
|
|---|
| 130 | -- 5. Event rating summary
|
|---|
| 131 | -- Used on public event pages.
|
|---|
| 132 | CREATE OR REPLACE VIEW public.v_event_rating_summary AS
|
|---|
| 133 | SELECT
|
|---|
| 134 | e.event_id,
|
|---|
| 135 | e.title AS event_title,
|
|---|
| 136 | COUNT(r.review_id) AS review_count,
|
|---|
| 137 | ROUND(AVG(r.star_rating), 2) AS average_rating,
|
|---|
| 138 | COUNT(*) FILTER (WHERE r.star_rating = 5) AS five_star_reviews,
|
|---|
| 139 | COUNT(*) FILTER (WHERE r.star_rating = 4) AS four_star_reviews,
|
|---|
| 140 | COUNT(*) FILTER (WHERE r.star_rating <= 2) AS negative_reviews,
|
|---|
| 141 | MAX(r.created_at) AS latest_review_at
|
|---|
| 142 | FROM public.event e
|
|---|
| 143 | LEFT JOIN public.review r
|
|---|
| 144 | ON r.event_id = e.event_id
|
|---|
| 145 | GROUP BY
|
|---|
| 146 | e.event_id,
|
|---|
| 147 | e.title;
|
|---|
| 148 |
|
|---|
| 149 | select *
|
|---|
| 150 | from v_event_rating_summary
|
|---|
| 151 | order by event_id;
|
|---|
| 152 |
|
|---|
| 153 | -- 6. Organiser performance dashboard
|
|---|
| 154 | -- Used internally by platform admins.
|
|---|
| 155 | CREATE OR REPLACE VIEW public.v_organiser_performance_dashboard AS
|
|---|
| 156 | SELECT
|
|---|
| 157 | o.organiser_id,
|
|---|
| 158 | o.company_name AS organiser_name,
|
|---|
| 159 | COUNT(DISTINCT e.event_id) AS total_events,
|
|---|
| 160 | COUNT(DISTINCT t.ticket_id) AS total_tickets_sold,
|
|---|
| 161 | COUNT(DISTINCT oc.order_id) AS total_orders,
|
|---|
| 162 | COALESCE(SUM(p.amount_paid), 0) AS total_revenue,
|
|---|
| 163 | ROUND(AVG(r.star_rating), 2) AS average_event_rating,
|
|---|
| 164 | COUNT(DISTINCT r.review_id) AS total_reviews
|
|---|
| 165 | FROM public.organiser o
|
|---|
| 166 | LEFT JOIN public.event e
|
|---|
| 167 | ON e.organiser_id = o.organiser_id
|
|---|
| 168 | LEFT JOIN public.ticket_type tt
|
|---|
| 169 | ON tt.event_id = e.event_id
|
|---|
| 170 | LEFT JOIN public.ticket t
|
|---|
| 171 | ON t.ticket_type_id = tt.ticket_type_id
|
|---|
| 172 | LEFT JOIN public.order_cart oc
|
|---|
| 173 | ON oc.order_id = t.order_id
|
|---|
| 174 | LEFT JOIN public.payment p
|
|---|
| 175 | ON p.order_id = oc.order_id
|
|---|
| 176 | LEFT JOIN public.review r
|
|---|
| 177 | ON r.event_id = e.event_id
|
|---|
| 178 | GROUP BY
|
|---|
| 179 | o.organiser_id,
|
|---|
| 180 | o.company_name;
|
|---|
| 181 |
|
|---|
| 182 | select *
|
|---|
| 183 | from v_organiser_performance_dashboard;
|
|---|
| 184 |
|
|---|
| 185 | -- 7. Location utilization summary
|
|---|
| 186 | -- Used by venue/location managers.
|
|---|
| 187 | CREATE OR REPLACE VIEW public.v_location_utilization_summary AS
|
|---|
| 188 | SELECT
|
|---|
| 189 | l.location_id,
|
|---|
| 190 | l.name AS location_name,
|
|---|
| 191 | lt.type_name AS location_type,
|
|---|
| 192 | COUNT(DISTINCT s.section_id) AS total_sections,
|
|---|
| 193 | COUNT(DISTINCT seat.seat_id) AS total_seats,
|
|---|
| 194 | COUNT(DISTINCT ess.schedule_id) AS scheduled_sessions,
|
|---|
| 195 | COUNT(DISTINCT e.event_id) AS hosted_events,
|
|---|
| 196 | COUNT(DISTINCT seat.seat_id) FILTER (WHERE seat.is_accessible = true) AS accessible_seats,
|
|---|
| 197 | COUNT(DISTINCT seat.seat_id) FILTER (WHERE seat.is_available = true) AS available_seats
|
|---|
| 198 | FROM public.location l
|
|---|
| 199 | JOIN public.location_type lt
|
|---|
| 200 | ON lt.type_id = l.type_id
|
|---|
| 201 | LEFT JOIN public.section s
|
|---|
| 202 | ON s.location_id = l.location_id
|
|---|
| 203 | LEFT JOIN public.seat seat
|
|---|
| 204 | ON seat.section_id = s.section_id
|
|---|
| 205 | LEFT JOIN public.event_schedule_session ess
|
|---|
| 206 | ON ess.section_id = s.section_id
|
|---|
| 207 | LEFT JOIN public.event e
|
|---|
| 208 | ON e.event_id = ess.event_id
|
|---|
| 209 | GROUP BY
|
|---|
| 210 | l.location_id,
|
|---|
| 211 | l.name,
|
|---|
| 212 | lt.type_name;
|
|---|
| 213 |
|
|---|
| 214 | select *
|
|---|
| 215 | from v_location_utilization_summary;
|
|---|
| 216 |
|
|---|
| 217 | -- 8. Refund analysis
|
|---|
| 218 | -- Used by finance/support team.
|
|---|
| 219 | CREATE OR REPLACE VIEW public.v_refund_analysis AS
|
|---|
| 220 | SELECT
|
|---|
| 221 | rr.refund_request_id,
|
|---|
| 222 | rr.requested_at,
|
|---|
| 223 | rr.accepted_at,
|
|---|
| 224 | CASE
|
|---|
| 225 | WHEN rr.accepted_at IS NOT NULL THEN 'ACCEPTED'
|
|---|
| 226 | ELSE 'PENDING'
|
|---|
| 227 | END AS refund_status,
|
|---|
| 228 | u.user_id,
|
|---|
| 229 | u.username,
|
|---|
| 230 | oc.order_id,
|
|---|
| 231 | p.payment_id,
|
|---|
| 232 | p.amount_paid,
|
|---|
| 233 | p.method_id,
|
|---|
| 234 | pm.method_name,
|
|---|
| 235 | rr.reason
|
|---|
| 236 | FROM public.refund_request rr
|
|---|
| 237 | JOIN public.user_app u
|
|---|
| 238 | ON u.user_id = rr.user_id
|
|---|
| 239 | JOIN public.payment p
|
|---|
| 240 | ON p.payment_id = rr.payment_id
|
|---|
| 241 | JOIN public.payment_method pm
|
|---|
| 242 | ON pm.method_id = p.method_id
|
|---|
| 243 | JOIN public.order_cart oc
|
|---|
| 244 | ON oc.order_id = p.order_id;
|
|---|
| 245 |
|
|---|
| 246 | select *
|
|---|
| 247 | from v_refund_analysis;
|
|---|
| 248 |
|
|---|
| 249 | -- 9. Event full search/details view
|
|---|
| 250 | -- Used by public event browsing/search page.
|
|---|
| 251 | CREATE OR REPLACE VIEW public.v_event_public_details AS
|
|---|
| 252 | SELECT
|
|---|
| 253 | e.event_id,
|
|---|
| 254 | e.title AS event_title,
|
|---|
| 255 | e.start_datetime,
|
|---|
| 256 | e.end_datetime,
|
|---|
| 257 | es.status_name AS event_status,
|
|---|
| 258 | o.company_name AS organiser_name,
|
|---|
| 259 |
|
|---|
| 260 | STRING_AGG(DISTINCT c.name, ', ') AS categories,
|
|---|
| 261 |
|
|---|
| 262 | COUNT(DISTINCT ess.schedule_id) AS session_count,
|
|---|
| 263 | COUNT(DISTINCT sp.sponsor_id) AS sponsor_count,
|
|---|
| 264 | COUNT(DISTINCT r.review_id) AS review_count,
|
|---|
| 265 | ROUND(AVG(r.star_rating), 2) AS average_rating,
|
|---|
| 266 |
|
|---|
| 267 | MIN(pt.price) AS lowest_ticket_price,
|
|---|
| 268 | MAX(pt.price) AS highest_ticket_price,
|
|---|
| 269 |
|
|---|
| 270 | COUNT(DISTINCT t.ticket_id) AS tickets_sold
|
|---|
| 271 | FROM public.event e
|
|---|
| 272 | JOIN public.event_status es
|
|---|
| 273 | ON es.event_status_id = e.event_status_id
|
|---|
| 274 | JOIN public.organiser o
|
|---|
| 275 | ON o.organiser_id = e.organiser_id
|
|---|
| 276 | LEFT JOIN public.event_category ec
|
|---|
| 277 | ON ec.event_id = e.event_id
|
|---|
| 278 | LEFT JOIN public.category c
|
|---|
| 279 | ON c.id = ec.category_id
|
|---|
| 280 | LEFT JOIN public.event_schedule_session ess
|
|---|
| 281 | ON ess.event_id = e.event_id
|
|---|
| 282 | LEFT JOIN public.sponsor_event se
|
|---|
| 283 | ON se.event_id = e.event_id
|
|---|
| 284 | LEFT JOIN public.sponsor sp
|
|---|
| 285 | ON sp.sponsor_id = se.sponsor_id
|
|---|
| 286 | LEFT JOIN public.review r
|
|---|
| 287 | ON r.event_id = e.event_id
|
|---|
| 288 | LEFT JOIN public.ticket_type tt
|
|---|
| 289 | ON tt.event_id = e.event_id
|
|---|
| 290 | LEFT JOIN public.price_tier pt
|
|---|
| 291 | ON pt.ticket_type_id = tt.ticket_type_id
|
|---|
| 292 | LEFT JOIN public.ticket t
|
|---|
| 293 | ON t.ticket_type_id = tt.ticket_type_id
|
|---|
| 294 | GROUP BY
|
|---|
| 295 | e.event_id,
|
|---|
| 296 | e.title,
|
|---|
| 297 | e.start_datetime,
|
|---|
| 298 | e.end_datetime,
|
|---|
| 299 | es.status_name,
|
|---|
| 300 | o.company_name;
|
|---|
| 301 |
|
|---|
| 302 | select *
|
|---|
| 303 | from v_event_public_details; |
|---|