| | 1 | = Budget vs Actual Expenditure Analysis = |
| | 2 | |
| | 3 | == 1. Scenario Overview == |
| | 4 | |
| | 5 | This scenario performs financial analysis by comparing planned wedding budgets against actual vendor expenditures. |
| | 6 | |
| | 7 | The report aggregates costs from: |
| | 8 | * venue bookings |
| | 9 | * photographer services |
| | 10 | * band entertainment |
| | 11 | |
| | 12 | The analysis enables: |
| | 13 | * budget tracking |
| | 14 | * financial reconciliation |
| | 15 | * identification of budget overruns |
| | 16 | * expenditure monitoring |
| | 17 | * variance calculation |
| | 18 | |
| | 19 | == 1.1 Objective == |
| | 20 | |
| | 21 | Perform comprehensive financial analysis comparing budgeted wedding costs against actual vendor expenditures. |
| | 22 | |
| | 23 | This report aggregates costs from venue bookings, photographer services, and band entertainment across all bookings associated with each wedding. |
| | 24 | |
| | 25 | The analysis enables identification of budget overruns, cost variances, and financial reconciliation at the wedding level. |
| | 26 | |
| | 27 | == 1.2 SQL Query Implementation == |
| | 28 | |
| | 29 | === SQL Code === |
| | 30 | |
| | 31 | {{{ |
| | 32 | #!sql |
| | 33 | SELECT |
| | 34 | w.wedding_id, |
| | 35 | u.first_name || ' ' || u.last_name AS organizer_name, |
| | 36 | w.date AS wedding_date, |
| | 37 | w.budget AS budgeted_amount, |
| | 38 | |
| | 39 | COALESCE(SUM(vb.price), 0) AS venue_cost, |
| | 40 | |
| | 41 | COALESCE(SUM( |
| | 42 | EXTRACT(EPOCH FROM (pb.end_time - pb.start_time))/3600 |
| | 43 | * p.price_per_hour |
| | 44 | ), 0) AS photographer_cost, |
| | 45 | |
| | 46 | COALESCE(SUM( |
| | 47 | EXTRACT(EPOCH FROM (bb.end_time - bb.start_time))/3600 |
| | 48 | * b.price_per_hour |
| | 49 | ), 0) AS band_cost, |
| | 50 | |
| | 51 | COALESCE(SUM(vb.price), 0) |
| | 52 | + COALESCE(SUM( |
| | 53 | EXTRACT(EPOCH FROM (pb.end_time - pb.start_time))/3600 |
| | 54 | * p.price_per_hour |
| | 55 | ), 0) |
| | 56 | + COALESCE(SUM( |
| | 57 | EXTRACT(EPOCH FROM (bb.end_time - bb.start_time))/3600 |
| | 58 | * b.price_per_hour |
| | 59 | ), 0) AS total_actual_cost, |
| | 60 | |
| | 61 | w.budget - ( |
| | 62 | COALESCE(SUM(vb.price), 0) |
| | 63 | + COALESCE(SUM( |
| | 64 | EXTRACT(EPOCH FROM (pb.end_time - pb.start_time))/3600 |
| | 65 | * p.price_per_hour |
| | 66 | ), 0) |
| | 67 | + COALESCE(SUM( |
| | 68 | EXTRACT(EPOCH FROM (bb.end_time - bb.start_time))/3600 |
| | 69 | * b.price_per_hour |
| | 70 | ), 0) |
| | 71 | ) AS remaining_budget, |
| | 72 | |
| | 73 | ROUND(( |
| | 74 | ( |
| | 75 | w.budget - ( |
| | 76 | COALESCE(SUM(vb.price), 0) |
| | 77 | + COALESCE(SUM( |
| | 78 | EXTRACT(EPOCH FROM (pb.end_time - pb.start_time))/3600 |
| | 79 | * p.price_per_hour |
| | 80 | ), 0) |
| | 81 | + COALESCE(SUM( |
| | 82 | EXTRACT(EPOCH FROM (bb.end_time - bb.start_time))/3600 |
| | 83 | * b.price_per_hour |
| | 84 | ), 0) |
| | 85 | ) |
| | 86 | ) / w.budget |
| | 87 | ) * 100, 2) AS budget_variance_percent |
| | 88 | |
| | 89 | FROM wedding w |
| | 90 | |
| | 91 | LEFT JOIN "user" u |
| | 92 | ON w.user_id = u.user_id |
| | 93 | |
| | 94 | LEFT JOIN venue_booking vb |
| | 95 | ON w.wedding_id = vb.wedding_id |
| | 96 | |
| | 97 | LEFT JOIN photographer_booking pb |
| | 98 | ON w.wedding_id = pb.wedding_id |
| | 99 | |
| | 100 | LEFT JOIN photographer p |
| | 101 | ON pb.photographer_id = p.photographer_id |
| | 102 | |
| | 103 | LEFT JOIN band_booking bb |
| | 104 | ON w.wedding_id = bb.wedding_id |
| | 105 | |
| | 106 | LEFT JOIN band b |
| | 107 | ON bb.band_id = b.band_id |
| | 108 | |
| | 109 | GROUP BY |
| | 110 | w.wedding_id, |
| | 111 | u.first_name, |
| | 112 | u.last_name, |
| | 113 | w.date, |
| | 114 | w.budget |
| | 115 | |
| | 116 | ORDER BY w.wedding_id; |
| | 117 | }}} |
| | 118 | |
| | 119 | == 1.3 Query Complexity Analysis == |
| | 120 | |
| | 121 | * Join Count: 7 tables |
| | 122 | * Aggregate Functions: SUM(), COALESCE(), EXTRACT(), ROUND() |
| | 123 | * Grouping Columns: wedding_id, organizer name, wedding date, and budget |
| | 124 | * Temporal Calculation: EXTRACT(EPOCH FROM ...) converts time intervals into billable hours |
| | 125 | * Financial Metrics: total cost, remaining budget, and variance percentage |
| | 126 | |
| | 127 | == 1.4 Relational Algebra Expression == |
| | 128 | |
| | 129 | {{{ |
| | 130 | π(w.wedding_id, u.fname, u.lname, w.date, w.budget, |
| | 131 | SUM(vb.price), |
| | 132 | SUM((pb.end - pb.start) * p.rate), |
| | 133 | SUM((bb.end - bb.start) * b.rate)) |
| | 134 | |
| | 135 | ( |
| | 136 | γ( |
| | 137 | wedding_id, |
| | 138 | SUM(venue_cost), |
| | 139 | SUM(photo_cost), |
| | 140 | SUM(band_cost) |
| | 141 | ) |
| | 142 | |
| | 143 | ( |
| | 144 | ρ( |
| | 145 | vb.price → venue_cost, |
| | 146 | (pb.end - pb.start) * p.price_per_hour → photo_cost, |
| | 147 | (bb.end - bb.start) * b.price_per_hour → band_cost |
| | 148 | ) |
| | 149 | |
| | 150 | ( |
| | 151 | (((Wedding ⟕ User) |
| | 152 | ⟕ Venue_Booking) |
| | 153 | ⟕ Photographer_Booking) |
| | 154 | ⟕ Photographer) |
| | 155 | ⟕ Band_Booking) |
| | 156 | ⟕ Band |
| | 157 | ) |
| | 158 | ) |
| | 159 | }}} |
| | 160 | |
| | 161 | === Notation === |
| | 162 | |
| | 163 | * π = Projection (SELECT clause) |
| | 164 | * γ = Grouping and aggregation (GROUP BY) |
| | 165 | * ⟕ = Left outer join (LEFT JOIN) |
| | 166 | * ρ = Rename operation (AS) |
| | 167 | * × = Cartesian product |
| | 168 | |
| | 169 | === Interpretation === |
| | 170 | |
| | 171 | The expression chains seven relations through left outer joins to preserve all weddings regardless of booking status. |
| | 172 | |
| | 173 | The grouping operation aggregates cost components by wedding_id, enabling dimensional financial analysis of expenditures. |
| | 174 | |
| | 175 | == 1.5 PostgreSQL Stored Procedure == |
| | 176 | |
| | 177 | === SQL Code === |
| | 178 | |
| | 179 | {{{ |
| | 180 | #!sql |
| | 181 | CREATE OR REPLACE PROCEDURE budget_variance_report( |
| | 182 | IN p_wedding_id INT DEFAULT NULL, |
| | 183 | IN p_start_date DATE DEFAULT '2020-01-01', |
| | 184 | IN p_end_date DATE DEFAULT '2099-12-31' |
| | 185 | ) |
| | 186 | LANGUAGE plpgsql |
| | 187 | AS $$ |
| | 188 | DECLARE |
| | 189 | v_record RECORD; |
| | 190 | v_venue_cost NUMERIC; |
| | 191 | v_photographer_cost NUMERIC; |
| | 192 | v_band_cost NUMERIC; |
| | 193 | v_total_cost NUMERIC; |
| | 194 | v_remaining NUMERIC; |
| | 195 | v_variance NUMERIC; |
| | 196 | |
| | 197 | BEGIN |
| | 198 | |
| | 199 | CREATE TEMP TABLE budget_variance_results ( |
| | 200 | wedding_id INTEGER, |
| | 201 | organizer_name VARCHAR, |
| | 202 | wedding_date DATE, |
| | 203 | budgeted_amount NUMERIC, |
| | 204 | venue_cost NUMERIC, |
| | 205 | photographer_cost NUMERIC, |
| | 206 | band_cost NUMERIC, |
| | 207 | total_actual_cost NUMERIC, |
| | 208 | remaining_budget NUMERIC, |
| | 209 | variance_percent NUMERIC |
| | 210 | ); |
| | 211 | |
| | 212 | FOR v_record IN |
| | 213 | |
| | 214 | SELECT |
| | 215 | w.wedding_id, |
| | 216 | u.first_name, |
| | 217 | u.last_name, |
| | 218 | w.date, |
| | 219 | w.budget |
| | 220 | |
| | 221 | FROM wedding w |
| | 222 | |
| | 223 | LEFT JOIN "user" u |
| | 224 | ON w.user_id = u.user_id |
| | 225 | |
| | 226 | WHERE |
| | 227 | (p_wedding_id IS NULL OR w.wedding_id = p_wedding_id) |
| | 228 | AND w.date BETWEEN p_start_date AND p_end_date |
| | 229 | |
| | 230 | LOOP |
| | 231 | |
| | 232 | SELECT COALESCE(SUM(vb.price), 0) |
| | 233 | INTO v_venue_cost |
| | 234 | FROM venue_booking vb |
| | 235 | WHERE vb.wedding_id = v_record.wedding_id; |
| | 236 | |
| | 237 | SELECT COALESCE(SUM( |
| | 238 | EXTRACT(EPOCH FROM (pb.end_time - pb.start_time))/3600 |
| | 239 | * p.price_per_hour |
| | 240 | ), 0) |
| | 241 | INTO v_photographer_cost |
| | 242 | |
| | 243 | FROM photographer_booking pb |
| | 244 | |
| | 245 | LEFT JOIN photographer p |
| | 246 | ON pb.photographer_id = p.photographer_id |
| | 247 | |
| | 248 | WHERE pb.wedding_id = v_record.wedding_id; |
| | 249 | |
| | 250 | SELECT COALESCE(SUM( |
| | 251 | EXTRACT(EPOCH FROM (bb.end_time - bb.start_time))/3600 |
| | 252 | * b.price_per_hour |
| | 253 | ), 0) |
| | 254 | INTO v_band_cost |
| | 255 | |
| | 256 | FROM band_booking bb |
| | 257 | |
| | 258 | LEFT JOIN band b |
| | 259 | ON bb.band_id = b.band_id |
| | 260 | |
| | 261 | WHERE bb.wedding_id = v_record.wedding_id; |
| | 262 | |
| | 263 | v_total_cost := v_venue_cost + v_photographer_cost + v_band_cost; |
| | 264 | |
| | 265 | v_remaining := v_record.budget - v_total_cost; |
| | 266 | |
| | 267 | v_variance := ROUND( |
| | 268 | (v_remaining / v_record.budget) * 100, |
| | 269 | 2 |
| | 270 | ); |
| | 271 | |
| | 272 | INSERT INTO budget_variance_results |
| | 273 | VALUES ( |
| | 274 | v_record.wedding_id, |
| | 275 | v_record.first_name || ' ' || v_record.last_name, |
| | 276 | v_record.date, |
| | 277 | v_record.budget, |
| | 278 | v_venue_cost, |
| | 279 | v_photographer_cost, |
| | 280 | v_band_cost, |
| | 281 | v_total_cost, |
| | 282 | v_remaining, |
| | 283 | v_variance |
| | 284 | ); |
| | 285 | |
| | 286 | END LOOP; |
| | 287 | |
| | 288 | RAISE NOTICE |
| | 289 | 'Budget Variance Report Generated - % rows', |
| | 290 | (SELECT COUNT(*) FROM budget_variance_results); |
| | 291 | |
| | 292 | END; |
| | 293 | $$; |
| | 294 | }}} |
| | 295 | |
| | 296 | == 1.6 Procedure Characteristics == |
| | 297 | |
| | 298 | * Input Parameters: |
| | 299 | * wedding ID (optional) |
| | 300 | * start date |
| | 301 | * end date |
| | 302 | |
| | 303 | * Iteration Logic: |
| | 304 | * cursor-based iteration through weddings |
| | 305 | |
| | 306 | * Financial Calculations: |
| | 307 | * isolated cost calculations for each vendor category |
| | 308 | |
| | 309 | * Error Handling: |
| | 310 | * RAISE NOTICE execution logging |
| | 311 | |
| | 312 | * Temporary Storage: |
| | 313 | * results stored in temporary session-scoped table |
| | 314 | |
| | 315 | == 1.7 Proof of Execution with Sample Data == |
| | 316 | |
| | 317 | === Sample Data Insertion === |
| | 318 | |
| | 319 | {{{ |
| | 320 | #!sql |
| | 321 | INSERT INTO "user" ( |
| | 322 | first_name, |
| | 323 | last_name, |
| | 324 | email, |
| | 325 | phone_number |
| | 326 | ) |
| | 327 | VALUES ( |
| | 328 | 'Michael', |
| | 329 | 'Richardson', |
| | 330 | 'michael.r@email.com', |
| | 331 | '555-0101' |
| | 332 | ); |
| | 333 | |
| | 334 | INSERT INTO wedding ( |
| | 335 | date, |
| | 336 | budget, |
| | 337 | user_id |
| | 338 | ) |
| | 339 | VALUES ( |
| | 340 | '2024-06-15', |
| | 341 | 8500.00, |
| | 342 | 1 |
| | 343 | ); |
| | 344 | |
| | 345 | INSERT INTO venue_type(type_name) |
| | 346 | VALUES ('Banquet Hall'); |
| | 347 | |
| | 348 | INSERT INTO venue ( |
| | 349 | name, |
| | 350 | location, |
| | 351 | city, |
| | 352 | address, |
| | 353 | capacity, |
| | 354 | price_per_guest, |
| | 355 | type_id |
| | 356 | ) |
| | 357 | VALUES ( |
| | 358 | 'Grand Vista', |
| | 359 | '123 Oak Street', |
| | 360 | 'Springfield', |
| | 361 | '123 Oak Street', |
| | 362 | 200, |
| | 363 | 45.00, |
| | 364 | 1 |
| | 365 | ); |
| | 366 | }}} |
| | 367 | |
| | 368 | === Query Execution Result === |
| | 369 | |
| | 370 | {{{ |
| | 371 | wedding_id | organizer_name | wedding_date | budgeted_amount |
| | 372 | ------------+---------------------+--------------+----------------- |
| | 373 | 1 | Michael Richardson | 2024-06-15 | 8500.00 |
| | 374 | }}} |
| | 375 | |
| | 376 | === Calculation Verification === |
| | 377 | |
| | 378 | * Venue Cost: fixed venue booking price |
| | 379 | * Photographer Cost: calculated using hourly duration |
| | 380 | * Band Cost: calculated using hourly duration |
| | 381 | * Total Actual Cost: sum of all vendor costs |
| | 382 | * Remaining Budget: planned budget minus total actual cost |
| | 383 | * Variance Percentage: remaining budget percentage relative to total budget |
| | 384 | |
| | 385 | == Summary == |
| | 386 | |
| | 387 | This scenario demonstrates advanced financial analysis using: |
| | 388 | * multi-table joins |
| | 389 | * aggregate calculations |
| | 390 | * temporal SQL calculations |
| | 391 | * stored procedures |
| | 392 | * relational algebra representation |
| | 393 | * reusable analytical reporting logic |