| | 209 | |
| | 210 | = Relational Algebra = |
| | 211 | |
| | 212 | |
| | 213 | == 6. Top selling routes for transport organizers == |
| | 214 | ==== Original Query |
| | 215 | {{{ |
| | 216 | CREATE VIEW top_selling_routes_view AS |
| | 217 | WITH route_stats AS ( |
| | 218 | SELECT |
| | 219 | r.route_id, |
| | 220 | from_loc.name AS from_location_name, |
| | 221 | to_loc.name AS to_location_name, |
| | 222 | to_org.company_name AS transport_organizer_name, |
| | 223 | COUNT(tk.ticket_id) AS total_tickets_sold, |
| | 224 | SUM(p.total_price)::numeric(38,2) AS total_revenue, |
| | 225 | AVG(p.total_price)::numeric(38,2) AS avg_ticket_price |
| | 226 | FROM route r |
| | 227 | JOIN transport_organizer to_org |
| | 228 | ON r.transport_organizer_id = to_org.transport_organizer_id |
| | 229 | JOIN location from_loc |
| | 230 | ON r.from_location_id = from_loc.location_id |
| | 231 | JOIN location to_loc |
| | 232 | ON r.to_location_id = to_loc.location_id |
| | 233 | JOIN trip tr ON r.route_id = tr.route_id |
| | 234 | JOIN ticket tk ON tr.trip_id = tk.trip_id |
| | 235 | JOIN payment p ON tk.payment_id = p.payment_id |
| | 236 | GROUP BY r.route_id, from_loc.name, to_loc.name, to_org.company_name |
| | 237 | ), |
| | 238 | max_tickets AS ( |
| | 239 | SELECT MAX(total_tickets_sold) AS max_sold |
| | 240 | FROM route_stats |
| | 241 | ) |
| | 242 | SELECT rs.* |
| | 243 | FROM route_stats rs, max_tickets mt |
| | 244 | ORDER BY rs.total_tickets_sold DESC; |
| | 245 | }}} |
| | 246 | |
| | 247 | == Results (Relational Algebra) == |
| | 248 | {{{ |
| | 249 | J1 = route ⨝ transport_organizer |
| | 250 | J2 = J1 ⨝ from_loc |
| | 251 | J3 = J2 ⨝ to_loc |
| | 252 | J4 = J3 ⨝ trip |
| | 253 | J5 = J4 ⨝ ticket |
| | 254 | J6 = J5 ⨝ payment |
| | 255 | |
| | 256 | G1 = γ(route_id, from_loc.name, to_loc.name, to_org.company_name; |
| | 257 | COUNT(ticket_id) → total_tickets_sold, |
| | 258 | SUM(total_price) → total_revenue, |
| | 259 | AVG(total_price) → avg_ticket_price)(J6) |
| | 260 | |
| | 261 | M1 = γ(; MAX(total_tickets_sold) → max_sold)(G1) |
| | 262 | |
| | 263 | Result = τ_desc(total_tickets_sold)(G1 × M1) |
| | 264 | }}} |
| | 265 | |
| | 266 | == Analysis (Optimizations) == |
| | 267 | * Push projections early to reduce carried attributes before aggregation. |
| | 268 | * Join order: start with fact tables (`ticket`, `payment`) then join dimension tables (`trip`, `route`, `location`, `transport_organizer`). |
| | 269 | * The `max_tickets` CTE is redundant unless we need to filter only top seller(s). |
| | 270 | * Indexing opportunities: |
| | 271 | - `ticket.trip_id`, `trip.route_id`, `ticket.payment_id` |
| | 272 | - Foreign keys: `route.from_location_id`, `route.to_location_id`, `route.transport_organizer_id` |
| | 273 | |
| | 274 | == Conclusion (Rewritten SQL) == |
| | 275 | {{{ |
| | 276 | CREATE VIEW top_selling_routes_view AS |
| | 277 | SELECT |
| | 278 | r.route_id, |
| | 279 | from_loc.name AS from_location_name, |
| | 280 | to_loc.name AS to_location_name, |
| | 281 | to_org.company_name AS transport_organizer_name, |
| | 282 | COUNT(tk.ticket_id) AS total_tickets_sold, |
| | 283 | SUM(p.total_price)::numeric(38,2) AS total_revenue, |
| | 284 | AVG(p.total_price)::numeric(38,2) AS avg_ticket_price |
| | 285 | FROM ticket tk |
| | 286 | JOIN payment p ON tk.payment_id = p.payment_id |
| | 287 | JOIN trip tr ON tk.trip_id = tr.trip_id |
| | 288 | JOIN route r ON tr.route_id = r.route_id |
| | 289 | JOIN transport_organizer to_org ON r.transport_organizer_id = to_org.transport_organizer_id |
| | 290 | JOIN location from_loc ON r.from_location_id = from_loc.location_id |
| | 291 | JOIN location to_loc ON r.to_location_id = to_loc.location_id |
| | 292 | GROUP BY r.route_id, from_loc.name, to_loc.name, to_org.company_name |
| | 293 | ORDER BY COUNT(tk.ticket_id) DESC; |
| | 294 | }}} |
| | 295 | |