DatabaseCreation: inserts.sql

File inserts.sql, 43.3 KB (added by 231178, 6 days ago)

DML

Line 
1-- SUPPLIERS (static, 5 suppliers)
2
3INSERT INTO Supplier (name, contact_name, phone, email, address) VALUES
4 ('PetCo Supply', 'James Turner', '+12125550101', 'james@petcosupply.com', '12 Commerce St, New York, NY 10001'),
5 ('FurFresh Co', 'Anna Schmidt', '+13105550202', 'anna@furfresh.com', '88 Sunset Blvd, Los Angeles, CA 90028'),
6 ('VetDirect', 'Carlos Reyes', '+13125550303', 'carlos@vetdirect.com', '5 Lake Ave, Chicago, IL 60601'),
7 ('AnimalPlus', 'Sara Novak', '+17135550404', 'sara@animalplus.com', '20 River Rd, Houston, TX 77001'),
8 ('PawMart', 'David Chen', '+16025550505', 'david@pawmart.com', '99 Desert Way, Phoenix, AZ 85001');
9
10
11-- CATEGORIES (static, 5 categories)
12
13INSERT INTO Category (name, description) VALUES
14 ('Food', 'Pet food and treats'),
15 ('Toys', 'Toys and entertainment items'),
16 ('Medicine', 'Health and medical supplies'),
17 ('Accessories', 'Collars, leashes and gear'),
18 ('Bedding', 'Beds, mats and sleeping items');
19
20
21-- ROOM TYPES (static, 5 room types)
22
23INSERT INTO Room_Type (name, description, price_per_night) VALUES
24 ('Economy', 'Basic room with essentials', 25.00),
25 ('Standard Single', 'Comfortable single-pet room', 45.00),
26 ('Standard Double', 'Room for two pets', 65.00),
27 ('Deluxe Suite', 'Spacious suite with premium amenities', 100.00),
28 ('VIP Penthouse', 'Top-tier suite with luxury comfort', 180.00);
29
30
31-- SERVICES (static, 8 services)
32
33INSERT INTO Service (name, description, price, duration_minutes) VALUES
34 ('Grooming', 'Full coat grooming session', 35.00, 60),
35 ('Vet Check', 'Basic health examination', 50.00, 30),
36 ('Training Session', 'One-on-one obedience training', 40.00, 45),
37 ('Daycare', 'Full day supervised care', 30.00, 480),
38 ('Bath & Dry', 'Wash and blow dry', 25.00, 45),
39 ('Nail Trim', 'Nail clipping and filing', 15.00, 20),
40 ('Dental Clean', 'Teeth brushing and cleaning', 20.00, 30),
41 ('Massage', 'Relaxation massage for pets', 30.00, 40);
42
43
44-- HOTELS (static, 10 hotels)
45
46INSERT INTO Hotel (name, location) VALUES
47 ('Paw Palace', 'New York'),
48 ('Cozy Paws', 'Los Angeles'),
49 ('Fur Haven', 'Chicago'),
50 ('Happy Tails', 'Houston'),
51 ('PetStay Plus', 'Phoenix'),
52 ('Bark and Relax', 'Seattle'),
53 ('The Woof Inn', 'Miami'),
54 ('Feline Retreat', 'Denver'),
55 ('Pawsome Lodge', 'Boston'),
56 ('Critter Comfort', 'Austin');
57
58
59-- EMPLOYEES (300, ~30 per hotel)
60
61INSERT INTO Employee (first_name, last_name, role, phone, email, hire_date, hotel_id)
62SELECT
63 fn.name,
64 ln.name,
65 (ARRAY['Vet','Groomer','Receptionist','Manager','Caretaker'])[floor(random()*5+1)::int],
66 '+1555' || (1000000000 + floor(random() * 9000000000)::bigint)::text,
67 lower(
68 regexp_replace(fn.name, '[^a-zA-Z0-9]', '', 'g') || '.' ||
69 regexp_replace(ln.name, '[^a-zA-Z0-9]', '', 'g') ||
70 gs || '@' || replace(lower(h.name), ' ', '') || '.com'
71 ),
72 CURRENT_DATE - (random() * 2000)::int,
73 h.hotel_id
74FROM generate_series(1, 300) gs
75 JOIN (
76 SELECT name, row_number() OVER (ORDER BY random()) AS fn_rn FROM first_names_male
77 UNION ALL
78 SELECT name, row_number() OVER (ORDER BY random()) + (SELECT COUNT(*) FROM first_names_male) FROM first_names_female
79) fn ON fn.fn_rn = (gs % ((SELECT COUNT(*) FROM first_names_male) + (SELECT COUNT(*) FROM first_names_female))::int) + 1
80 JOIN (
81 SELECT name, row_number() OVER (ORDER BY random()) AS ln_rn FROM last_names
82) ln ON ln.ln_rn = (gs % (SELECT COUNT(*) FROM last_names)::int) + 1
83 JOIN (
84 SELECT hotel_id, name, row_number() OVER (ORDER BY random()) AS h_rn FROM Hotel
85) h ON h.h_rn = (gs % (SELECT COUNT(*) FROM Hotel)::int) + 1;
86
87
88-- ROOMS (2,500 total, 250 per hotel)
89
90INSERT INTO Room (room_number, capacity, room_type_id, hotel_id)
91SELECT
92 h.hotel_id || '-' ||
93 CASE
94 WHEN gs <= 83 THEN 'A'
95 WHEN gs <= 166 THEN 'B'
96 ELSE 'C'
97 END || (100 + gs)::text,
98 floor(random() * 3 + 1)::int,
99 ((gs - 1) % (SELECT COUNT(*) FROM Room_Type)::int) + 1,
100 h.hotel_id
101FROM Hotel h
102 CROSS JOIN generate_series(1, 250) gs;
103
104
105-- CUSTOMERS (150,000)
106
107INSERT INTO Customer (first_name, last_name, email, phone, address, registration_date)
108SELECT
109 fn.name,
110 ln.name,
111 lower(
112 regexp_replace(fn.name, '[^a-zA-Z0-9]', '', 'g') || '.' ||
113 regexp_replace(ln.name, '[^a-zA-Z0-9]', '', 'g') ||
114 gs || '@customer.com'
115 ),
116 '+1555' || (1000000000 + floor(random() * 9000000000)::bigint)::text,
117 a.address,
118 CURRENT_DATE - (random() * 1500)::int
119FROM generate_series(1, 150000) gs
120 JOIN (
121 SELECT name, row_number() OVER (ORDER BY random()) AS fn_rn FROM first_names_male
122 UNION ALL
123 SELECT name, row_number() OVER (ORDER BY random()) + (SELECT COUNT(*) FROM first_names_male) FROM first_names_female
124) fn ON fn.fn_rn = (gs % ((SELECT COUNT(*) FROM first_names_male) + (SELECT COUNT(*) FROM first_names_female))::int) + 1
125 JOIN (
126 SELECT name, row_number() OVER (ORDER BY random()) AS ln_rn FROM last_names
127) ln ON ln.ln_rn = (gs % (SELECT COUNT(*) FROM last_names)::int) + 1
128 JOIN (
129 SELECT address, row_number() OVER (ORDER BY random()) AS rn FROM addresses
130) a ON a.rn = (gs % (SELECT COUNT(*) FROM addresses)::int) + 1;
131
132
133-- PRODUCTS (500)
134
135INSERT INTO Product (name, description, price, category_id, supplier_id)
136SELECT
137 p.product_name || ' #' || gs,
138 'Quality pet product - ' || p.product_name,
139 round((random() * 95 + 5)::numeric, 2),
140 (SELECT category_id FROM Category WHERE name = p.category_name),
141 supplier_id
142FROM generate_series(1, 500) gs
143 JOIN (
144 VALUES
145 ('Premium Kibble', 'Food'),
146 ('Chew Toy', 'Toys'),
147 ('Flea Collar', 'Medicine'),
148 ('Plush Bed', 'Bedding'),
149 ('Catnip', 'Toys'),
150 ('Harness', 'Accessories'),
151 ('Bowl Set', 'Accessories'),
152 ('Vitamin Drops', 'Medicine'),
153 ('Leash', 'Accessories'),
154 ('Shampoo', 'Medicine')
155) AS p(product_name, category_name)
156 ON p.product_name = (
157 ARRAY[
158 'Premium Kibble','Chew Toy','Flea Collar','Plush Bed','Catnip',
159 'Harness','Bowl Set','Vitamin Drops','Leash','Shampoo'
160 ]
161 )[(gs % 10) + 1]
162 CROSS JOIN LATERAL (
163 SELECT supplier_id
164 FROM Supplier
165 ORDER BY md5(gs::text || 'sup' || random()::text)
166 LIMIT 1
167 ) c2(supplier_id);
168
169
170-- PETS (15,000)
171
172INSERT INTO Pet (name, date_of_birth, gender, customer_id, species_id, breed_id)
173SELECT
174 pet_name,
175 CURRENT_DATE - (random() * 5000)::int,
176 (ARRAY['Male','Female'])[floor(random() * 2 + 1)::int],
177 cust.customer_id,
178 s.species_id,
179 b.breed_id
180FROM generate_series(15001, 200000) gs
181 CROSS JOIN LATERAL (
182 SELECT val AS pet_name FROM (
183 SELECT val, row_number() OVER (ORDER BY random()) AS rn
184 FROM unnest(ARRAY[
185 'Buddy','Max','Bella','Lucy','Charlie','Daisy',
186 'Milo','Luna','Rocky','Coco','Oscar','Lily',
187 'Teddy','Zoe','Loki','Nala','Bear','Stella',
188 'Archie','Molly','Bailey','Cooper','Sadie','Duke',
189 'Rosie','Tucker','Maggie','Zeus','Penny','Bruno'
190 ]::text[]) AS t(val)
191 ) sub WHERE rn = (gs % 30) + 1
192 ) names
193 CROSS JOIN LATERAL (
194 SELECT species_id FROM (
195 SELECT species_id, row_number() OVER (ORDER BY random()) AS rn FROM Species
196 ) sub WHERE rn = (gs % (SELECT COUNT(*) FROM Species)::int) + 1
197 ) s
198 CROSS JOIN LATERAL (
199 SELECT breed_id FROM (
200 SELECT breed_id, row_number() OVER (ORDER BY random()) AS rn
201 FROM Breed WHERE species_id = s.species_id
202 ) sub WHERE rn = (gs % (SELECT COUNT(*) FROM Breed WHERE species_id = s.species_id)::int) + 1
203 ) b
204 CROSS JOIN LATERAL (
205 SELECT customer_id FROM (
206 SELECT customer_id, row_number() OVER (ORDER BY random()) AS rn FROM Customer
207 ) sub WHERE rn = (gs % (SELECT COUNT(*) FROM Customer)::int) + 1
208 ) cust;
209
210
211-- ORDERS (45,000)
212
213INSERT INTO "Order" (order_date, status, total_amount, customer_id, hotel_id)
214SELECT
215 CURRENT_DATE - (random() * 1000)::int,
216 (ARRAY['pending','confirmed','completed','cancelled'])[floor(random() * 4 + 1)::int],
217 round((random() * 500 + 10)::numeric, 2),
218 c.customer_id,
219 h.hotel_id
220FROM generate_series(1, 45000) gs
221 CROSS JOIN LATERAL (
222 SELECT customer_id FROM (
223 SELECT customer_id, row_number() OVER (ORDER BY random()) AS rn FROM Customer
224 ) sub WHERE rn = (gs % (SELECT COUNT(*) FROM Customer)::int) + 1
225 ) c
226 CROSS JOIN LATERAL (
227 SELECT hotel_id FROM (
228 SELECT hotel_id, row_number() OVER (ORDER BY random()) AS rn FROM Hotel
229 ) sub WHERE rn = (gs % (SELECT COUNT(*) FROM Hotel)::int) + 1
230 ) h;
231
232
233-- ORDER PRODUCTS (~23,000)
234
235INSERT INTO OrderProduct (quantity, unit_price, order_id, product_id)
236SELECT
237 (random() * 9 + 1)::int,
238 (random() * 100 + 1)::numeric(10,2),
239 o.order_id,
240 p.product_id
241FROM (
242 SELECT order_id, row_number() OVER (ORDER BY random()) AS rn
243 FROM "Order"
244 LIMIT 23000
245 ) o
246 JOIN (
247 SELECT product_id, row_number() OVER (ORDER BY random()) AS rn,
248 COUNT(*) OVER () AS total
249 FROM Product
250) p ON p.rn = (
251 ('x' || substr(md5(o.order_id::text || o.rn::text), 1, 8))::bit(32)::int % p.total + 1
252 )
253ON CONFLICT (order_id, product_id) DO NOTHING;
254
255
256-- RESERVATIONS (7,000,000)
257-- reservation_date is set to the check-in date (2015–2027)
258-- so it stays consistent with RoomReservation.check_in_date.
259DO $$
260 BEGIN
261 FOR iter IN 0..99 LOOP
262 INSERT INTO Reservation (
263 reservation_date,
264 status,
265 notes,
266 total_cost,
267 pet_id,
268 employee_id
269 )
270 WITH
271 pets AS (
272 SELECT
273 pet_id,
274 row_number() OVER (ORDER BY pet_id) - 1 AS rn,
275 count(*) OVER () AS total
276 FROM Pet
277 ),
278 emps AS (
279 SELECT
280 employee_id,
281 row_number() OVER (ORDER BY employee_id) - 1 AS rn,
282 count(*) OVER () AS total
283 FROM Employee
284 ),
285 statuses(idx, val) AS (
286 VALUES
287 (1,'confirmed'),
288 (2,'confirmed'),
289 (3,'confirmed'),
290 (4,'confirmed'),
291 (5,'confirmed'),
292 (6,'confirmed'),
293 (7,'completed'),
294 (8,'completed'),
295 (9,'completed'),
296 (10,'completed'),
297 (11,'pending'),
298 (12,'pending'),
299 (13,'cancelled'),
300 (14,'cancelled'),
301 (15,'no-show')
302 ),
303 notes(idx, val) AS (
304 VALUES
305 (0,'Owner requests daily photo updates.'),
306 (1,'Pet is on a special diet - dry food only.'),
307 (2,'Nervous around loud noises, please keep calm.'),
308 (3,'Very friendly with other animals.'),
309 (4,'Requires medication twice daily.'),
310 (5,NULL),
311 (6,NULL),
312 (7,NULL)
313 )
314 SELECT
315 DATE '2015-01-01'
316 + floor(random() * 4747)::int,
317 s.val,
318 n.val,
319 floor(random() * 800 + 50)::int,
320 p.pet_id,
321 e.employee_id
322 FROM generate_series(1, 100000) gs(n)
323 JOIN statuses s
324 ON s.idx = (gs.n % 15) + 1
325 JOIN notes n
326 ON n.idx = (gs.n * 3 + 5) % 8
327 JOIN pets p
328 ON p.rn = (gs.n * 5 + 2)
329 % (SELECT total FROM pets LIMIT 1)
330 JOIN emps e
331 ON e.rn = (gs.n * 7 + 11)
332 % (SELECT total FROM emps LIMIT 1);
333 END LOOP;
334 END;
335$$;
336
337
338DROP TABLE RoomReservation;
339
340CREATE TABLE RoomReservation (
341 room_reservation_id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY,
342 check_in_date DATE NOT NULL,
343 check_out_date DATE NOT NULL,
344 reservation_id INTEGER NOT NULL,
345 room_id INTEGER NOT NULL,
346 PRIMARY KEY (room_reservation_id),
347 CONSTRAINT uq_roomreservation_reservation
348 UNIQUE (reservation_id),
349 CONSTRAINT chk_roomres_dates
350 CHECK (check_out_date > check_in_date),
351 CONSTRAINT no_overlapping_room_reservations
352 EXCLUDE USING GIST (
353 room_id WITH =,
354 daterange(check_in_date, check_out_date, '[)') WITH &&
355 ),
356 CONSTRAINT fk_roomres_room
357 FOREIGN KEY (room_id) REFERENCES Room(room_id)
358 ON DELETE RESTRICT ON UPDATE CASCADE,
359 CONSTRAINT fk_roomres_reservation
360 FOREIGN KEY (reservation_id) REFERENCES Reservation(reservation_id)
361 ON DELETE CASCADE ON UPDATE CASCADE
362);
363
364-- Step 2: Insert
365DO $$
366 BEGIN
367 FOR iter IN 0..99 LOOP
368 INSERT INTO RoomReservation (
369 check_in_date,
370 check_out_date,
371 reservation_id,
372 room_id
373 )
374 WITH rooms AS (
375 SELECT
376 room_id,
377 (row_number() OVER (ORDER BY room_id) - 1)::int AS room_index,
378 count(*) OVER ()::int AS total_rooms
379 FROM Room
380 ),
381 reservations AS (
382 SELECT
383 reservation_id,
384 reservation_date,
385 (iter * 100000) + (row_number() OVER (ORDER BY reservation_id) - 1)::int AS global_index
386 FROM (
387 SELECT reservation_id, reservation_date
388 FROM Reservation
389 ORDER BY reservation_id
390 LIMIT 100000
391 OFFSET (iter * 100000)
392 ) x
393 ),
394 assigned AS (
395 SELECT
396 r.reservation_id,
397 r.reservation_date,
398 rm.room_id,
399 rm.total_rooms,
400 r.global_index,
401 floor(random() * 6 + 1)::int AS stay_length
402 FROM reservations r
403 JOIN rooms rm
404 ON rm.room_index = r.global_index % rm.total_rooms
405 ),
406 scheduled AS (
407 SELECT
408 reservation_id,
409 room_id,
410 DATE '2015-01-01'
411 + ((global_index / total_rooms) * 8)::int AS check_in_date,
412 DATE '2015-01-01'
413 + ((global_index / total_rooms) * 8)::int
414 + stay_length AS check_out_date
415 FROM assigned
416 )
417 SELECT
418 check_in_date,
419 check_out_date,
420 reservation_id,
421 room_id
422 FROM scheduled
423 ORDER BY room_id, check_in_date
424 ON CONFLICT (reservation_id) DO NOTHING;
425
426 RAISE NOTICE 'Completed iteration % of 99 (offset %)', iter, iter * 100000;
427 END LOOP;
428 END;
429$$;
430
431-- Step 3: Verify
432SELECT COUNT(*) FROM RoomReservation; -- should be ~10,000,000
433
434
435
436
437
438SET work_mem = '512MB';
439SET synchronous_commit = OFF;
440
441DO $$
442 BEGIN
443 FOR iter IN 0..99 LOOP
444 INSERT INTO RoomReservation (
445 check_in_date,
446 check_out_date,
447 reservation_id,
448 room_id
449 )
450 WITH rooms AS (
451 SELECT
452 room_id,
453 (row_number() OVER (ORDER BY room_id) - 1)::int AS room_index,
454 count(*) OVER ()::int AS total_rooms
455 FROM Room
456 ),
457 reservations AS (
458 SELECT
459 reservation_id,
460 reservation_date,
461 (row_number() OVER (ORDER BY reservation_id) - 1)::int AS res_index
462 FROM (
463 SELECT reservation_id, reservation_date
464 FROM Reservation
465 ORDER BY reservation_id
466 LIMIT 100000
467 OFFSET (iter * 100000)
468 ) x
469 ),
470 assigned AS (
471 SELECT
472 r.reservation_id,
473 r.reservation_date,
474 rm.room_id,
475 floor(random() * 6 + 1)::int AS stay_length
476 FROM reservations r
477 JOIN rooms rm
478 ON rm.room_index = r.res_index % rm.total_rooms
479 ),
480 scheduled AS (
481 SELECT
482 reservation_id,
483 room_id,
484 reservation_date AS check_in_date,
485 reservation_date + stay_length AS check_out_date
486 FROM assigned
487 )
488 SELECT
489 check_in_date,
490 check_out_date,
491 reservation_id,
492 room_id
493 FROM scheduled
494 ON CONFLICT (reservation_id) DO NOTHING;
495
496 RAISE NOTICE 'Completed iteration % of 99 (offset %)', iter, iter * 100000;
497 END LOOP;
498 END;
499$$;
500
501-- Step 7: Reset settings
502SET synchronous_commit = ON;
503SET work_mem = '4MB';
504
505-- Reset settings
506SET synchronous_commit = ON;
507SET work_mem = '4MB';
508
509-- Verify
510SELECT COUNT(*) FROM Date;
511
512-- SERVICE RESERVATIONS
513-- ~60 % of RoomReservations get at least one service.
514-- Each selected reservation gets 1–3 services at random,
515-- scheduled on a random day within the actual room stay.
516WITH reservation_sample AS (
517 SELECT
518 rr.reservation_id,
519 rr.check_in_date,
520 rr.check_out_date,
521 -- Assign number of services: 0, 1, 2, or 3
522 -- 40% = 0, 35% = 1, 18% = 2, 7% = 3
523 CASE
524 WHEN random() < 0.40 THEN 0
525 WHEN random() < 0.58 THEN 1 -- 35% of remaining
526 WHEN random() < 0.75 THEN 2 -- 18% of remaining
527 ELSE 3
528 END AS num_services
529 FROM RoomReservation rr
530 WHERE rr.check_out_date > rr.check_in_date
531),
532 expanded AS (
533 SELECT
534 rs.reservation_id,
535 rs.check_in_date,
536 rs.check_out_date,
537 gs.service_num
538 FROM reservation_sample rs
539 CROSS JOIN generate_series(1, rs.num_services) gs(service_num)
540 WHERE rs.num_services > 0
541 ),
542 services_numbered AS (
543 SELECT
544 service_id,
545 row_number() OVER (ORDER BY service_id) - 1 AS svc_index,
546 count(*) OVER () AS total_services
547 FROM Service
548 )
549INSERT INTO ServiceReservation (
550 scheduled_date,
551 scheduled_time,
552 status,
553 reservation_id,
554 service_id
555)
556SELECT
557 e.check_in_date
558 + floor(random() * GREATEST(e.check_out_date - e.check_in_date, 1))::int
559 AS scheduled_date,
560 (ARRAY[
561 '09:00','10:00','11:00','12:00','13:00',
562 '14:00','15:00','16:00','17:00'
563 ])[floor(random() * 9 + 1)::int]::time AS scheduled_time,
564 (ARRAY['scheduled','completed','cancelled','no-show'])[floor(random()*4 + 1)::int],
565 e.reservation_id,
566 s.service_id
567FROM expanded e
568 JOIN services_numbered s
569 ON s.svc_index = (e.reservation_id * e.service_num * 7 + e.service_num * 13)
570 % (SELECT total_services FROM services_numbered LIMIT 1)
571ON CONFLICT DO NOTHING;
572
573-- EMPLOYEE–SERVICE (up to 240 unique pairs)
574
575INSERT INTO Employee_Service (employee_id, service_id)
576SELECT DISTINCT e.employee_id, s.service_id
577FROM generate_series(1, 1000) gs
578 JOIN (SELECT employee_id, row_number() OVER (ORDER BY random()) AS rn FROM Employee) e
579 ON e.rn = (gs % (SELECT COUNT(*) FROM Employee)::int) + 1
580 JOIN (SELECT service_id, row_number() OVER (ORDER BY random()) AS rn FROM Service) s
581 ON s.rn = (gs % (SELECT COUNT(*) FROM Service)::int) + 1
582ON CONFLICT DO NOTHING;
583
584
585-- PRODUCT–SERVICE (up to 120 unique pairs)
586
587INSERT INTO Product_Service (product_id, service_id)
588SELECT DISTINCT p.product_id, s.service_id
589FROM generate_series(1, 400) gs
590 JOIN (SELECT product_id, row_number() OVER (ORDER BY random()) AS rn FROM Product) p
591 ON p.rn = (gs % (SELECT COUNT(*) FROM Product)::int) + 1
592 JOIN (SELECT service_id, row_number() OVER (ORDER BY random()) AS rn FROM Service) s
593 ON s.rn = (gs % (SELECT COUNT(*) FROM Service)::int) + 1
594ON CONFLICT DO NOTHING;
595
596
597-- REVIEWS (150,000)
598
599INSERT INTO Review (review_date, rating, comment, customer_id, hotel_id)
600SELECT
601 CURRENT_DATE - (random() * 365)::int,
602 rating,
603 CASE
604 WHEN rating >= 9 THEN (ARRAY[
605 'Absolutely incredible experience — our dog was treated like royalty!',
606 'Best pet hotel we have ever used. The staff went above and beyond.',
607 'Our cat came home happy, clean, and clearly well-loved. 10 out of 10!',
608 'Exceptional service from check-in to check-out. Will never go anywhere else.',
609 'The VIP suite was worth every penny. Our pup was in heaven!',
610 'Flawless experience. Staff remembered our pet by name on the second visit.',
611 'I was nervous leaving my rabbit for the first time, but the team was amazing.',
612 'Five stars is not enough. Truly the gold standard of pet care.',
613 'Our senior dog received such gentle and attentive care. We are so grateful.',
614 'Immaculate facilities, caring staff, and our pets came home thriving.'
615 ])[floor(random() * 10 + 1)::int]
616
617 WHEN rating >= 7 THEN (ARRAY[
618 'Really great stay overall. The staff were friendly and clearly love animals.',
619 'Our dog seemed very happy when we picked him up. Clean rooms, good service.',
620 'Solid experience. Would definitely recommend to other pet owners.',
621 'Great grooming service included — our cat looked fabulous afterwards.',
622 'Very professional team. Our pet was well cared for throughout the stay.',
623 'Good facilities and attentive staff. Only minor issue was parking.',
624 'Our pets had a wonderful time. The play area is a great touch.',
625 'Really impressed with the daily photo updates. Gave us great peace of mind.',
626 'Friendly reception, clean rooms, and our dog ate well the whole time.',
627 'Above average in every way. We have already booked our next stay.'
628 ])[floor(random() * 10 + 1)::int]
629
630 WHEN rating >= 5 THEN (ARRAY[
631 'Decent place overall. Nothing spectacular but the pets were looked after.',
632 'Average experience. Staff were polite but not particularly attentive.',
633 'Rooms were clean enough. Our dog seemed fine but not especially happy.',
634 'It does the job. Would consider returning if prices stay reasonable.',
635 'Fairly standard pet hotel. No complaints but no real highlights either.',
636 'Middle of the road. Communication could be better but the care was okay.',
637 'Our cat was safe and fed, which is what matters. Facilities felt a bit dated.',
638 'Okay for the price. Do not expect luxury but basics are covered.',
639 'Mixed feelings. Some staff were great, others less engaged.',
640 'Not bad, not great. We might try somewhere else next time just to compare.'
641 ])[floor(random() * 10 + 1)::int]
642
643 WHEN rating >= 3 THEN (ARRAY[
644 'Disappointing visit. Our dog seemed stressed when we picked him up.',
645 'Rooms were smaller than advertised and not very clean.',
646 'Staff seemed overwhelmed and communication was poor throughout.',
647 'Expected much more for the price. Facilities felt neglected.',
648 'Our pet came home with a minor scratch — no explanation was given.',
649 'Booking was easy but the actual experience did not match the website.',
650 'Not enough staff on duty. Our pet did not get the attention promised.',
651 'Unimpressive. The grooming service was rushed and below standard.',
652 'Several issues during the stay that were not handled well by management.',
653 'Would not rush back. There are better options in the area for less money.'
654 ])[floor(random() * 10 + 1)::int]
655
656 ELSE (ARRAY[
657 'Terrible experience. Our pet came home visibly distressed.',
658 'Absolutely awful. Filthy rooms, rude staff, zero accountability.',
659 'I would not leave a goldfish here. Deeply disappointed and angry.',
660 'Our dog lost weight during the stay. Clear sign of neglect.',
661 'Staff were dismissive when we raised concerns. Totally unacceptable.',
662 'The worst pet hotel we have ever used. Avoid at all costs.',
663 'Disgusting conditions. Reported to local animal welfare authorities.',
664 'No care, no communication, no apology. A complete waste of money.',
665 'Our cat was returned to us dehydrated. Shocking level of negligence.',
666 'One star is too generous. This place should not be operating.'
667 ])[floor(random() * 10 + 1)::int]
668 END,
669 c.customer_id,
670 h.hotel_id
671FROM (
672 SELECT
673 gs,
674 floor(random() * 10 + 1)::int AS rating
675 FROM generate_series(1, 150000) gs
676 ) rated
677 CROSS JOIN LATERAL (
678 SELECT customer_id FROM (
679 SELECT customer_id, row_number() OVER (ORDER BY random()) AS rn FROM Customer
680 ) sub WHERE rn = (rated.gs % (SELECT COUNT(*) FROM Customer)::int) + 1
681 ) c
682 CROSS JOIN LATERAL (
683 SELECT hotel_id FROM (
684 SELECT hotel_id, row_number() OVER (ORDER BY random()) AS rn FROM Hotel
685 ) sub WHERE rn = (rated.gs % (SELECT COUNT(*) FROM Hotel)::int) + 1
686 ) h;
687
688
689-- MEDICAL RECORDS (300,000)
690
691INSERT INTO MedicalRecord (record_date, status, vet_name, alergies, medications, note, diagnosis, pet_id)
692SELECT
693 CURRENT_DATE - (random() * 730)::int,
694 (ARRAY['healthy','healthy','sick','recovering'])[floor(random() * 4 + 1)::int],
695 'Dr. ' || ln.name,
696 (ARRAY[NULL, 'Pollen', 'Dust mites', 'Certain proteins', 'Flea bites'])[floor(random() * 5 + 1)::int],
697 (ARRAY[NULL, 'Antibiotic', 'Anti-inflammatory', 'Antiparasitic', 'Vitamin supplement'])[floor(random() * 5 + 1)::int],
698 (ARRAY[NULL, 'Monitor weight weekly.', 'Avoid stressful environments.', 'Follow-up in 2 weeks.'])[floor(random() * 4 + 1)::int],
699 (ARRAY['Routine checkup', 'Mild infection', 'Skin irritation', 'Dental issue', 'Ear infection', 'Overweight'])[floor(random() * 6 + 1)::int],
700 pet.pet_id
701FROM generate_series(1, 300000) gs
702 JOIN (
703 SELECT name, row_number() OVER (ORDER BY random()) AS ln_rn FROM last_names
704) ln ON ln.ln_rn = (gs % (SELECT COUNT(*) FROM last_names)::int) + 1
705 JOIN (
706 SELECT pet_id, row_number() OVER (ORDER BY random()) AS rn FROM Pet
707) pet ON pet.rn = (gs % (SELECT COUNT(*) FROM Pet)::int) + 1;
708
709
710-- PET DELIVERIES (80,000)
711
712INSERT INTO PetDelivery (delivery_time, status, destination, reservation_id, employee_id)
713SELECT
714 NOW() - (random() * 365 * INTERVAL '1 day'),
715 st.val,
716 dest.val,
717 r.reservation_id,
718 e.employee_id
719FROM (
720 SELECT reservation_id, row_number() OVER (ORDER BY random()) AS rn
721 FROM Reservation
722 LIMIT 80000
723 ) r
724 CROSS JOIN LATERAL (
725 SELECT val FROM (
726 SELECT val, row_number() OVER (ORDER BY random()) AS rn
727 FROM unnest(ARRAY['scheduled','delivered','failed','confirmed']::text[]) AS t(val)
728 ) sub WHERE rn = (r.rn % 4) + 1
729 ) st
730 CROSS JOIN LATERAL (
731 SELECT val FROM (
732 SELECT val, row_number() OVER (ORDER BY random()) AS rn
733 FROM unnest(ARRAY['home','hotel','vet clinic','airport']::text[]) AS t(val)
734 ) sub WHERE rn = (r.rn % 4) + 1
735 ) dest
736 CROSS JOIN LATERAL (
737 SELECT employee_id FROM (
738 SELECT employee_id, row_number() OVER (ORDER BY random()) AS rn FROM Employee
739 ) sub WHERE rn = (r.rn % (SELECT COUNT(*) FROM Employee)::int) + 1
740 ) e;
741
742
743-- PAYMENTS (~30,000)
744
745SET work_mem = '512MB';
746SET synchronous_commit = OFF;
747
748DO $$
749 BEGIN
750 FOR iter IN 0..99 LOOP
751 INSERT INTO Payment (payment_date, amount, payment_method, status, reservation_id)
752 SELECT
753 r.reservation_date + floor(random() * 3)::int,
754 r.total_cost,
755 (ARRAY['credit card','debit card','cash','bank transfer','PayPal'])[floor(random() * 5 + 1)::int],
756 (ARRAY['completed','completed','completed','completed','refunded','pending'])[floor(random() * 6 + 1)::int],
757 r.reservation_id
758 FROM (
759 SELECT reservation_id, reservation_date, total_cost
760 FROM Reservation
761 WHERE status IN ('confirmed', 'completed')
762 ORDER BY reservation_id
763 LIMIT 100000
764 OFFSET (iter * 100000)
765 ) r;
766
767 RAISE NOTICE 'Completed iteration % of 99 (offset %)', iter, iter * 100000;
768 END LOOP;
769 END;
770$$;
771
772SET synchronous_commit = ON;
773SET work_mem = '4MB';
774
775-- Verify
776SELECT COUNT(*) FROM Payment;
777
778-- DELIVERIES (¬23,000)
779
780INSERT INTO Delivery (delivered_at, status, hotel_id, purchase_id)
781SELECT
782 (ARRAY['morning','afternoon','evening','overnight'])[floor(random() * 4 + 1)::int],
783 (ARRAY['pending','in_transit','delivered','failed'])[floor(random() * 4 + 1)::int],
784 o.hotel_id,
785 op.orderproduct_id
786FROM OrderProduct op
787 JOIN "Order" o ON o.order_id = op.order_id;
788
789
790-- VIEW: v_room_availability
791
792CREATE OR REPLACE VIEW v_room_availability AS
793SELECT
794 h.hotel_id,
795 h.name AS hotel_name,
796 h.location,
797 r.room_id,
798 r.room_number,
799 r.capacity,
800 rt.name AS room_type,
801 rt.price_per_night,
802 d.date,
803 d.status AS availability_status,
804 CASE WHEN d.status = 'occupied' THEN TRUE ELSE FALSE END AS is_occupied,
805 CASE WHEN d.status = 'available' THEN TRUE ELSE FALSE END AS is_available,
806 CASE WHEN d.status = 'maintenance' THEN TRUE ELSE FALSE END AS in_maintenance
807FROM Room r
808 JOIN Hotel h ON h.hotel_id = r.hotel_id
809 JOIN Room_Type rt ON rt.room_type_id = r.room_type_id
810 LEFT JOIN Date d ON d.room_id = r.room_id;
811
812
813
814
815
816
817-- TOCNITE ROOM RESERVATION I DATE INSERTS
818
819
820
821
822-- Step 2: Recreate RoomReservation without exclusion constraint
823CREATE TABLE RoomReservation (
824 room_reservation_id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY,
825 check_in_date DATE NOT NULL,
826 check_out_date DATE NOT NULL,
827 reservation_id INTEGER NOT NULL,
828 room_id INTEGER NOT NULL,
829 PRIMARY KEY (room_reservation_id),
830 CONSTRAINT uq_roomreservation_reservation
831 UNIQUE (reservation_id),
832 CONSTRAINT chk_roomres_dates
833 CHECK (check_out_date > check_in_date),
834 CONSTRAINT fk_roomres_room
835 FOREIGN KEY (room_id) REFERENCES Room(room_id)
836 ON DELETE RESTRICT ON UPDATE CASCADE,
837 CONSTRAINT fk_roomres_reservation
838 FOREIGN KEY (reservation_id) REFERENCES Reservation(reservation_id)
839 ON DELETE CASCADE ON UPDATE CASCADE
840);
841
842-- Step 3: Recreate Date table
843CREATE TABLE Date (
844 date DATE NOT NULL,
845 room_id INTEGER NOT NULL,
846 status VARCHAR(20),
847 PRIMARY KEY (date, room_id),
848 CONSTRAINT fk_dates_room
849 FOREIGN KEY (room_id) REFERENCES Room(room_id)
850 ON DELETE RESTRICT ON UPDATE CASCADE
851);
852
853-- Step 4: Insert RoomReservations
854SET work_mem = '512MB';
855SET synchronous_commit = OFF;
856
857DO $$
858 BEGIN
859 FOR iter IN 0..99 LOOP
860 INSERT INTO RoomReservation (
861 check_in_date,
862 check_out_date,
863 reservation_id,
864 room_id
865 )
866 WITH rooms AS (
867 SELECT
868 room_id,
869 (row_number() OVER (ORDER BY room_id) - 1)::int AS room_index,
870 count(*) OVER ()::int AS total_rooms
871 FROM Room
872 ),
873 reservations AS (
874 SELECT
875 reservation_id,
876 reservation_date,
877 (row_number() OVER (ORDER BY reservation_id) - 1)::int AS res_index
878 FROM (
879 SELECT reservation_id, reservation_date
880 FROM Reservation
881 ORDER BY reservation_id
882 LIMIT 100000
883 OFFSET (iter * 100000)
884 ) x
885 ),
886 assigned AS (
887 SELECT
888 r.reservation_id,
889 r.reservation_date,
890 rm.room_id,
891 floor(random() * 6 + 1)::int AS stay_length
892 FROM reservations r
893 JOIN rooms rm
894 ON rm.room_index = r.res_index % rm.total_rooms
895 ),
896 scheduled AS (
897 SELECT
898 reservation_id,
899 room_id,
900 reservation_date AS check_in_date,
901 reservation_date + stay_length AS check_out_date
902 FROM assigned
903 )
904 SELECT
905 check_in_date,
906 check_out_date,
907 reservation_id,
908 room_id
909 FROM scheduled
910 ON CONFLICT (reservation_id) DO NOTHING;
911
912 RAISE NOTICE 'Completed iteration % of 99 (offset %)', iter, iter * 100000;
913 END LOOP;
914 END;
915$$;
916
917-- Step 5: Verify RoomReservation
918SELECT COUNT(*) FROM RoomReservation; -- should be ~10,000,000
919SELECT MIN(check_in_date), MAX(check_out_date) FROM RoomReservation; -- should be within 2015-2027
920
921-- Step 6: Insert Date table
922DO $$
923 DECLARE
924 r_min INTEGER;
925 r_max INTEGER;
926 batch_size INTEGER := 100;
927 cur INTEGER;
928 BEGIN
929 SELECT MIN(room_id), MAX(room_id) INTO r_min, r_max FROM Room;
930 cur := r_min;
931
932 WHILE cur <= r_max LOOP
933
934 -- Part A: occupied nights from RoomReservation
935 INSERT INTO Date (date, room_id, status)
936 SELECT DISTINCT ON (gs::date, rr.room_id)
937 gs::date,
938 rr.room_id,
939 'occupied'
940 FROM RoomReservation rr
941 CROSS JOIN LATERAL generate_series(
942 rr.check_in_date,
943 rr.check_out_date - 1,
944 interval '1 day'
945 ) gs
946 WHERE rr.room_id BETWEEN cur AND cur + batch_size - 1
947 ORDER BY gs::date, rr.room_id
948 ON CONFLICT (date, room_id) DO UPDATE SET status = 'occupied';
949
950 -- Part B: fill remaining dates as available/maintenance
951 INSERT INTO Date (date, room_id, status)
952 SELECT
953 d::date,
954 r.room_id,
955 (ARRAY['available','available','available','maintenance'])[floor(random()*4+1)::int]
956 FROM Room r
957 CROSS JOIN generate_series(
958 DATE '2015-01-01',
959 DATE '2027-12-31',
960 interval '1 day'
961 ) AS d
962 WHERE r.room_id BETWEEN cur AND cur + batch_size - 1
963 ON CONFLICT (date, room_id) DO NOTHING;
964
965 RAISE NOTICE 'Processed rooms % to %', cur, cur + batch_size - 1;
966 cur := cur + batch_size;
967
968 END LOOP;
969 END;
970$$;
971
972-- Step 7: Reset settings
973SET synchronous_commit = ON;
974SET work_mem = '4MB';
975
976-- Step 8: Verify Date table
977SELECT COUNT(*) FROM Date; -- should be ~11,870,000
978SELECT MIN(date), MAX(date) FROM Date; -- should be 2015-01-01 to 2027-12-31
979
980
981
982
983
984SELECT 'Species' AS table_name, COUNT(*) FROM Species UNION ALL
985SELECT 'Breed', COUNT(*) FROM Breed UNION ALL
986SELECT 'Category', COUNT(*) FROM Category UNION ALL
987SELECT 'Room_Type', COUNT(*) FROM Room_Type UNION ALL
988SELECT 'Service', COUNT(*) FROM Service UNION ALL
989SELECT 'Hotel', COUNT(*) FROM Hotel UNION ALL
990SELECT 'Employee', COUNT(*) FROM Employee UNION ALL
991SELECT 'Room', COUNT(*) FROM Room UNION ALL
992SELECT 'Customer', COUNT(*) FROM Customer UNION ALL
993SELECT 'Pet', COUNT(*) FROM Pet UNION ALL
994SELECT 'Product', COUNT(*) FROM Product UNION ALL
995SELECT 'Supplier', COUNT(*) FROM Supplier UNION ALL
996SELECT 'Order', COUNT(*) FROM "Order" UNION ALL
997SELECT 'OrderProduct', COUNT(*) FROM OrderProduct UNION ALL
998SELECT 'Delivery', COUNT(*) FROM Delivery UNION ALL
999SELECT 'Reservation', COUNT(*) FROM Reservation UNION ALL
1000SELECT 'RoomReservation', COUNT(*) FROM RoomReservation UNION ALL
1001SELECT 'ServiceReservation', COUNT(*) FROM ServiceReservation UNION ALL
1002SELECT 'Payment', COUNT(*) FROM Payment UNION ALL
1003SELECT 'Review', COUNT(*) FROM Review UNION ALL
1004SELECT 'MedicalRecord', COUNT(*) FROM MedicalRecord UNION ALL
1005SELECT 'PetDelivery', COUNT(*) FROM PetDelivery UNION ALL
1006SELECT 'Date', COUNT(*) FROM Date
1007ORDER BY table_name;