DatabaseProgramming: Code_phase4.sql

File Code_phase4.sql, 19.4 KB (added by 231061, 5 days ago)
Line 
1
2-- FUNCTION: Креирање trip
3
4CREATE OR REPLACE FUNCTION fn_create_trip(
5 p_user_id INT,
6 p_city_name VARCHAR,
7 p_start_date DATE,
8 p_end_date DATE,
9 p_total_budget DECIMAL(10,2),
10 p_title VARCHAR DEFAULT NULL
11)
12RETURNS INT
13LANGUAGE plpgsql
14AS $$
15DECLARE
16 v_city_id INT;
17 v_status_id INT;
18 v_trip_id INT;
19BEGIN
20 IF p_start_date IS NULL THEN
21 RAISE EXCEPTION 'Start date is required.';
22 END IF;
23
24 IF p_end_date IS NULL THEN
25 RAISE EXCEPTION 'End date is required.';
26 END IF;
27
28 IF p_end_date < p_start_date THEN
29 RAISE EXCEPTION 'End date cannot be before start date.';
30 END IF;
31
32 IF p_total_budget <= 0 THEN
33 RAISE EXCEPTION 'Budget must be greater than 0.';
34 END IF;
35
36 SELECT city_id
37 INTO v_city_id
38 FROM city
39 WHERE LOWER(name) = LOWER(p_city_name)
40 LIMIT 1;
41
42 IF v_city_id IS NULL THEN
43 RAISE EXCEPTION 'City "%" does not exist.', p_city_name;
44 END IF;
45
46 IF NOT EXISTS (
47 SELECT 1
48 FROM app_user
49 WHERE user_id = p_user_id
50 ) THEN
51 RAISE EXCEPTION 'User with id % does not exist.', p_user_id;
52 END IF;
53
54 INSERT INTO trip_status (
55 user_id,
56 status_name,
57 status_comment,
58 updated_at
59 )
60 VALUES (
61 p_user_id,
62 'in_progress',
63 'Trip created',
64 NOW()
65 )
66 RETURNING status_id INTO v_status_id;
67
68 INSERT INTO trip (
69 user_id,
70 city_id,
71 status_id,
72 history_id,
73 title,
74 start_date,
75 end_date,
76 total_budget,
77 estimated_cost
78 )
79 VALUES (
80 p_user_id,
81 v_city_id,
82 v_status_id,
83 NULL,
84 COALESCE(p_title, 'Trip to ' || p_city_name),
85 p_start_date,
86 p_end_date,
87 p_total_budget,
88 0
89 )
90 RETURNING trip_id INTO v_trip_id;
91
92 RETURN v_trip_id;
93END;
94$$;
95
96
97-- test
98
99SELECT fn_create_trip(
100 1,
101 'City_7',
102 '2026-08-01',
103 '2026-08-05',
104 700.00,
105 'August Skopje Trip'
106) AS new_trip_id;
107
108SELECT *
109FROM trip
110WHERE trip_id = 4523757 ;
111
112
113
114-- FUNCTION: Најди trip_id
115
116CREATE OR REPLACE FUNCTION fn_find_trip_id(
117 p_username VARCHAR,
118 p_city_name VARCHAR,
119 p_start_date DATE,
120 p_end_date DATE DEFAULT NULL,
121 p_title VARCHAR DEFAULT NULL
122)
123RETURNS INT
124LANGUAGE plpgsql
125AS $$
126DECLARE
127 v_trip_id INT;
128BEGIN
129 SELECT t.trip_id
130 INTO v_trip_id
131 FROM trip t
132 JOIN app_user u ON t.user_id = u.user_id
133 JOIN city c ON t.city_id = c.city_id
134 WHERE LOWER(u.username) = LOWER(p_username)
135 AND LOWER(c.name) = LOWER(p_city_name)
136 AND t.start_date = p_start_date
137 AND (p_end_date IS NULL OR t.end_date = p_end_date)
138 AND (
139 p_title IS NULL
140 OR LOWER(t.title) = LOWER(p_title)
141 )
142 ORDER BY t.trip_id DESC
143 LIMIT 1;
144
145 IF v_trip_id IS NULL THEN
146 RAISE EXCEPTION 'Trip for user "%", city "%", start date "%", title "%" does not exist.',
147 p_username, p_city_name, p_start_date, p_title;
148 END IF;
149
150 RETURN v_trip_id;
151END;
152$$;
153
154
155-- test
156
157SELECT
158 CASE
159 WHEN fn_find_trip_id(
160 u.username,
161 c.name,
162 t.start_date,
163 t.end_date,
164 t.title
165 ) = t.trip_id
166 THEN 'TEST PASSED'
167 ELSE 'TEST FAILED'
168 END AS test_result,
169 t.trip_id
170FROM trip t
171JOIN app_user u ON t.user_id = u.user_id
172JOIN city c ON t.city_id = c.city_id
173LIMIT 5;
174
175
176
177-- FUNCTION: Пресметување вкупен estimated cost за trip
178
179CREATE OR REPLACE FUNCTION fn_calculate_trip_estimated_cost(
180 p_username VARCHAR,
181 p_city_name VARCHAR,
182 p_start_date DATE,
183 p_end_date DATE DEFAULT NULL,
184 p_title VARCHAR DEFAULT NULL
185)
186RETURNS DECIMAL(10,2)
187LANGUAGE plpgsql
188AS $$
189DECLARE
190 v_trip_id INT;
191 v_total_cost DECIMAL(10,2);
192BEGIN
193 v_trip_id := fn_find_trip_id(
194 p_username,
195 p_city_name,
196 p_start_date,
197 p_end_date,
198 p_title
199 );
200
201 SELECT COALESCE(SUM(pi.estimated_cost), 0)
202 INTO v_total_cost
203 FROM plan_item pi
204 JOIN trip_day td ON pi.trip_day_id = td.trip_day_id
205 WHERE td.trip_id = v_trip_id;
206
207 RETURN v_total_cost;
208END;
209$$;
210
211
212-- test
213
214SELECT
215 u.username,
216 c.name AS city_name,
217 t.start_date,
218 t.end_date,
219 t.title,
220 t.trip_id
221FROM trip t
222JOIN app_user u ON t.user_id = u.user_id
223JOIN city c ON t.city_id = c.city_id
224ORDER BY t.trip_id DESC
225LIMIT 10;
226
227
228
229-- PROCEDURE: Додавање trip member
230
231CREATE OR REPLACE PROCEDURE pr_add_trip_member(
232 p_owner_username VARCHAR,
233 p_city_name VARCHAR,
234 p_start_date DATE,
235 p_member_username VARCHAR,
236 p_title VARCHAR DEFAULT NULL
237)
238LANGUAGE plpgsql
239AS $$
240DECLARE
241 v_trip_id INT;
242 v_member_user_id INT;
243 v_member_first_name VARCHAR;
244 v_member_last_name VARCHAR;
245 v_trip_member_id INT;
246BEGIN
247 -- 1. Најди го trip_id
248 v_trip_id := fn_find_trip_id(
249 p_owner_username,
250 p_city_name,
251 p_start_date,
252 NULL,
253 p_title
254 );
255
256 -- 2. Најди user податоци за member username
257 SELECT
258 user_id,
259 first_name,
260 last_name
261 INTO
262 v_member_user_id,
263 v_member_first_name,
264 v_member_last_name
265 FROM app_user
266 WHERE LOWER(username) = LOWER(p_member_username);
267
268 IF v_member_user_id IS NULL THEN
269 RAISE EXCEPTION 'Member user with username "%" does not exist.', p_member_username;
270 END IF;
271
272 -- 3. Провери дали member веќе е додаден во trip
273 IF EXISTS (
274 SELECT 1
275 FROM trip_member
276 WHERE trip_id = v_trip_id
277 AND user_id = v_member_user_id
278 ) THEN
279 RAISE EXCEPTION 'User "%" is already a member of trip %.',
280 p_member_username, v_trip_id;
281 END IF;
282
283 -- 4. Додај member во trip
284 INSERT INTO trip_member (
285 trip_id,
286 user_id,
287 first_name,
288 last_name,
289 username,
290 role
291 )
292 VALUES (
293 v_trip_id,
294 v_member_user_id,
295 v_member_first_name,
296 v_member_last_name,
297 p_member_username,
298 'member'
299 )
300 RETURNING trip_member_id INTO v_trip_member_id;
301
302 RAISE NOTICE 'User "%" added to trip "%" successfully. Trip member id: %',
303 p_member_username, v_trip_id, v_trip_member_id;
304END;
305$$;
306
307CALL pr_add_trip_member(
308 'user_1',
309 'City_1',
310 '2026-05-16',
311 'user_64192',
312 Null
313);
314
315
316-- test
317
318SELECT
319 tm.trip_member_id,
320 tm.trip_id,
321 tm.user_id,
322 tm.first_name,
323 tm.last_name,
324 tm.username,
325 tm.role,
326 t.title,
327 t.start_date,
328 c.name AS city_name
329FROM trip_member tm
330JOIN trip t ON tm.trip_id = t.trip_id
331JOIN city c ON t.city_id = c.city_id
332ORDER BY tm.trip_member_id DESC
333LIMIT 5;
334
335
336
337-- PROCEDURE: Додавање plan item во trip
338
339CREATE OR REPLACE PROCEDURE pr_add_plan_item_to_trip(
340 p_username VARCHAR,
341 p_city_name VARCHAR,
342 p_start_date DATE,
343 p_day_number INT,
344 p_place_name VARCHAR,
345 p_start_time TIME,
346 p_end_time TIME,
347 p_estimated_cost DECIMAL(10,2),
348 p_notes TEXT DEFAULT NULL,
349 p_title VARCHAR DEFAULT NULL
350)
351LANGUAGE plpgsql
352AS $$
353DECLARE
354 v_trip_id INT;
355 v_trip_day_id INT;
356 v_place_id INT;
357 v_schedule_time_id INT;
358 v_plan_item_id INT;
359 v_current_estimated_cost DECIMAL(10,2);
360 v_total_budget DECIMAL(10,2);
361BEGIN
362 IF p_day_number <= 0 THEN
363 RAISE EXCEPTION 'Day number must be greater than 0.';
364 END IF;
365
366 IF p_end_time <= p_start_time THEN
367 RAISE EXCEPTION 'End time must be after start time.';
368 END IF;
369
370 IF p_estimated_cost < 0 THEN
371 RAISE EXCEPTION 'Estimated cost cannot be negative.';
372 END IF;
373
374 v_trip_id := fn_find_trip_id(
375 p_username,
376 p_city_name,
377 p_start_date,
378 NULL,
379 p_title
380 );
381
382 SELECT trip_day_id
383 INTO v_trip_day_id
384 FROM trip_day
385 WHERE trip_id = v_trip_id
386 AND day_number = p_day_number;
387
388 IF v_trip_day_id IS NULL THEN
389 RAISE EXCEPTION 'Day % does not exist for this trip.', p_day_number;
390 END IF;
391
392 SELECT p.place_id
393 INTO v_place_id
394 FROM place p
395 JOIN trip t ON p.city_id = t.city_id
396 WHERE t.trip_id = v_trip_id
397 AND LOWER(p.name) = LOWER(p_place_name)
398 LIMIT 1;
399
400 IF v_place_id IS NULL THEN
401 RAISE EXCEPTION 'Place "%" does not exist in the trip city.', p_place_name;
402 END IF;
403
404 INSERT INTO schedule_time (
405 scheduled_start_time,
406 scheduled_end_time
407 )
408 VALUES (
409 p_start_time,
410 p_end_time
411 )
412 RETURNING schedule_time_id INTO v_schedule_time_id;
413
414 INSERT INTO plan_item (
415 trip_day_id,
416 place_id,
417 schedule_time_id,
418 estimated_cost,
419 notes
420 )
421 VALUES (
422 v_trip_day_id,
423 v_place_id,
424 v_schedule_time_id,
425 p_estimated_cost,
426 p_notes
427 )
428 RETURNING plan_item_id INTO v_plan_item_id;
429
430 SELECT COALESCE(SUM(pi.estimated_cost), 0)
431 INTO v_current_estimated_cost
432 FROM plan_item pi
433 JOIN trip_day td ON pi.trip_day_id = td.trip_day_id
434 WHERE td.trip_id = v_trip_id;
435
436 SELECT total_budget
437 INTO v_total_budget
438 FROM trip
439 WHERE trip_id = v_trip_id;
440
441 IF v_current_estimated_cost >= v_total_budget THEN
442 RAISE EXCEPTION 'Cannot update trip estimated cost because it reaches/exceeds total budget.';
443 END IF;
444
445 UPDATE trip
446 SET estimated_cost = v_current_estimated_cost
447 WHERE trip_id = v_trip_id;
448
449 RAISE NOTICE 'Plan item added successfully. Plan item id: %. New trip estimated cost: %',
450 v_plan_item_id, v_current_estimated_cost;
451END;
452$$;
453
454
455CALL pr_add_plan_item_to_trip(
456 'user_1',
457 'City_1',
458 '2026-05-16',
459 1,
460 'Place_1068',
461 '10:00',
462 '12:00',
463 100,
464 'Visit and take photos',
465 Null
466);
467
468
469-- test
470
471SELECT
472 u.user_id,
473 u.username,
474 c.city_id,
475 c.name AS city_name,
476 t.trip_id,
477 t.title,
478 t.start_date,
479 t.end_date,
480 t.total_budget,
481 t.estimated_cost,
482 td.trip_day_id,
483 td.day_number,
484 p.place_id,
485 p.name AS place_name
486FROM app_user u
487JOIN trip t ON u.user_id = t.user_id
488JOIN city c ON t.city_id = c.city_id
489JOIN trip_day td ON t.trip_id = td.trip_id
490JOIN place p ON p.city_id = c.city_id
491WHERE LOWER(u.username) = LOWER('user_1')
492 AND LOWER(c.name) = LOWER('City_1')
493 AND t.start_date = '2026-05-16'
494 AND td.day_number = 1
495 AND LOWER(p.name) = LOWER('Place_1068');
496
497
498
499-- PROCEDURE: Додавање place во favourites
500
501CREATE OR REPLACE PROCEDURE pr_add_place_to_favourites(
502 p_username VARCHAR,
503 p_place_name VARCHAR
504)
505LANGUAGE plpgsql
506AS $$
507DECLARE
508 v_user_id INT;
509 v_place_id INT;
510BEGIN
511 SELECT user_id
512 INTO v_user_id
513 FROM app_user
514 WHERE LOWER(username) = LOWER(p_username);
515
516 IF v_user_id IS NULL THEN
517 RAISE EXCEPTION 'User with username "%" does not exist.', p_username;
518 END IF;
519
520 SELECT place_id
521 INTO v_place_id
522 FROM place
523 WHERE LOWER(name) = LOWER(p_place_name)
524 LIMIT 1;
525
526 IF v_place_id IS NULL THEN
527 RAISE EXCEPTION 'Place "%" does not exist.', p_place_name;
528 END IF;
529
530 IF EXISTS (
531 SELECT 1
532 FROM favourites
533 WHERE user_id = v_user_id
534 AND place_id = v_place_id
535 ) THEN
536 RAISE EXCEPTION 'Place "%" is already in favourites for user "%".',
537 p_place_name, p_username;
538 END IF;
539
540 INSERT INTO favourites (
541 user_id,
542 place_id
543 )
544 VALUES (
545 v_user_id,
546 v_place_id
547 );
548
549 RAISE NOTICE 'Place "%" added to favourites for user "%".',
550 p_place_name, p_username;
551END;
552$$;
553
554
555CALL pr_add_place_to_favourites(
556 'user_417155',
557 'Place_854864'
558);
559
560
561-- test
562
563SELECT
564 f.user_id,
565 u.username,
566 f.place_id,
567 p.name AS place_name
568FROM favourites f
569JOIN app_user u ON f.user_id = u.user_id
570JOIN place p ON f.place_id = p.place_id
571WHERE LOWER(u.username) = LOWER('user_417155')
572 AND LOWER(p.name) = LOWER('Place_854864');
573
574
575
576-- PROCEDURE: Додавање review за place од trip
577
578CREATE OR REPLACE PROCEDURE pr_add_review_for_trip_place(
579 p_username VARCHAR,
580 p_city_name VARCHAR,
581 p_start_date DATE,
582 p_day_number INT,
583 p_place_name VARCHAR,
584 p_rating DECIMAL(3,2),
585 p_comment TEXT DEFAULT NULL,
586 p_title VARCHAR DEFAULT NULL
587)
588LANGUAGE plpgsql
589AS $$
590DECLARE
591 v_user_id INT;
592 v_trip_id INT;
593 v_plan_item_id INT;
594 v_review_id INT;
595BEGIN
596 IF p_rating < 1 OR p_rating > 5 THEN
597 RAISE EXCEPTION 'Rating must be between 1 and 5.';
598 END IF;
599
600 SELECT user_id
601 INTO v_user_id
602 FROM app_user
603 WHERE LOWER(username) = LOWER(p_username);
604
605 IF v_user_id IS NULL THEN
606 RAISE EXCEPTION 'User with username "%" does not exist.', p_username;
607 END IF;
608
609 v_trip_id := fn_find_trip_id(
610 p_username,
611 p_city_name,
612 p_start_date,
613 NULL,
614 p_title
615 );
616
617 SELECT pi.plan_item_id
618 INTO v_plan_item_id
619 FROM plan_item pi
620 JOIN trip_day td ON pi.trip_day_id = td.trip_day_id
621 JOIN place p ON pi.place_id = p.place_id
622 WHERE td.trip_id = v_trip_id
623 AND td.day_number = p_day_number
624 AND LOWER(p.name) = LOWER(p_place_name)
625 LIMIT 1;
626
627 IF v_plan_item_id IS NULL THEN
628 RAISE EXCEPTION 'Plan item for place "%" on day % does not exist.',
629 p_place_name, p_day_number;
630 END IF;
631
632 INSERT INTO review (
633 user_id,
634 plan_item_id,
635 rating,
636 comment,
637 visited_at,
638 created_at
639 )
640 VALUES (
641 v_user_id,
642 v_plan_item_id,
643 p_rating,
644 p_comment,
645 NOW(),
646 NOW()
647 )
648 RETURNING review_id INTO v_review_id;
649
650 RAISE NOTICE 'Review added successfully. Review id: %', v_review_id;
651END;
652$$;
653
654
655CALL pr_add_review_for_trip_place(
656 'user_417155',
657 'City_246',
658 '2025-11-10',
659 2,
660 'Place_854864',
661 3,
662 NULL,
663 'Trip_1999999'
664);
665
666
667-- test
668
669SELECT
670 CASE
671 WHEN p.rating = ROUND(AVG(r.rating), 2)
672 THEN 'TEST PASSED: place.rating is correctly updated'
673 ELSE 'TEST FAILED: place.rating is not equal to average review rating'
674 END AS test_result,
675 p.place_id,
676 p.name AS place_name,
677 p.rating AS current_place_rating,
678 ROUND(AVG(r.rating), 2) AS calculated_avg_rating,
679 COUNT(r.review_id) AS total_reviews
680FROM place p
681JOIN plan_item pi ON p.place_id = pi.place_id
682LEFT JOIN review r ON pi.plan_item_id = r.plan_item_id
683WHERE p.place_id = (
684 SELECT pi2.place_id
685 FROM review r2
686 JOIN plan_item pi2 ON r2.plan_item_id = pi2.plan_item_id
687 ORDER BY r2.review_id DESC
688 LIMIT 1
689)
690GROUP BY
691 p.place_id,
692 p.name,
693 p.rating;
694
695
696
697-- PROCEDURE: Комплетирање trip и креирање trip history
698
699CREATE OR REPLACE PROCEDURE pr_complete_trip(
700 p_username VARCHAR,
701 p_city_name VARCHAR,
702 p_start_date DATE,
703 p_end_date DATE DEFAULT NULL,
704 p_title VARCHAR DEFAULT NULL,
705 p_notes TEXT DEFAULT 'Trip completed successfully'
706)
707LANGUAGE plpgsql
708AS $$
709DECLARE
710 v_trip_id INT;
711 v_user_id INT;
712 v_total_spent DECIMAL(10,2);
713 v_history_id INT;
714 v_status_id INT;
715BEGIN
716 SELECT user_id
717 INTO v_user_id
718 FROM app_user
719 WHERE LOWER(username) = LOWER(p_username);
720
721 IF v_user_id IS NULL THEN
722 RAISE EXCEPTION 'User with username "%" does not exist.', p_username;
723 END IF;
724
725 v_trip_id := fn_find_trip_id(
726 p_username,
727 p_city_name,
728 p_start_date,
729 p_end_date,
730 p_title
731 );
732
733 SELECT estimated_cost
734 INTO v_total_spent
735 FROM trip
736 WHERE trip_id = v_trip_id;
737
738 IF v_total_spent IS NULL THEN
739 RAISE EXCEPTION 'Trip not found.';
740 END IF;
741
742 INSERT INTO trip_history (
743 user_id,
744 total_spent,
745 notes,
746 completed_at
747 )
748 VALUES (
749 v_user_id,
750 v_total_spent,
751 p_notes,
752 NOW()
753 )
754 RETURNING history_id INTO v_history_id;
755
756 INSERT INTO trip_status (
757 user_id,
758 status_name,
759 status_comment,
760 updated_at
761 )
762 VALUES (
763 v_user_id,
764 'completed',
765 'Trip marked as completed',
766 NOW()
767 )
768 RETURNING status_id INTO v_status_id;
769
770 UPDATE trip
771 SET history_id = v_history_id,
772 status_id = v_status_id
773 WHERE trip_id = v_trip_id;
774
775 RAISE NOTICE 'Trip % completed successfully. History id: %, Status id: %',
776 v_trip_id, v_history_id, v_status_id;
777END;
778$$;
779
780
781
782CALL pr_complete_trip(
783 'user_88113',
784 'City_119',
785 '2025-12-07',
786 '2025-12-18',
787 NULL,
788 'Trip completed successfully'
789);
790
791
792-- test
793
794SELECT
795 CASE
796 WHEN ts.status_name = 'completed'
797 AND th.history_id IS NOT NULL
798 THEN 'TEST PASSED: Trip is completed'
799 ELSE 'TEST FAILED: Trip is not completed'
800 END AS test_result,
801 t.trip_id,
802 ts.status_name,
803 th.history_id,
804 th.total_spent,
805 th.notes
806FROM trip t
807JOIN app_user u ON t.user_id = u.user_id
808JOIN city c ON t.city_id = c.city_id
809JOIN trip_status ts ON t.status_id = ts.status_id
810JOIN trip_history th ON t.history_id = th.history_id
811WHERE LOWER(u.username) = LOWER('user_88113')
812 AND LOWER(c.name) = LOWER('City_119')
813 AND t.start_date = '2025-12-07'
814 AND t.end_date = '2025-12-18';
815
816
817
818
819-- TRIGGER FUNCTION: Ажурирање rating на place кога ќе се додаде/промени/избрише review
820
821CREATE OR REPLACE FUNCTION fn_update_place_rating_after_review()
822RETURNS TRIGGER
823LANGUAGE plpgsql
824AS $$
825DECLARE
826 v_place_id INT;
827 v_avg_rating DECIMAL(3,2);
828BEGIN
829 -- Ако е DELETE, земаме OLD.plan_item_id
830 IF TG_OP = 'DELETE' THEN
831 SELECT pi.place_id
832 INTO v_place_id
833 FROM plan_item pi
834 WHERE pi.plan_item_id = OLD.plan_item_id;
835 ELSE
836 SELECT pi.place_id
837 INTO v_place_id
838 FROM plan_item pi
839 WHERE pi.plan_item_id = NEW.plan_item_id;
840 END IF;
841
842 IF v_place_id IS NULL THEN
843 RETURN COALESCE(NEW, OLD);
844 END IF;
845
846 SELECT COALESCE(ROUND(AVG(r.rating), 2), 0)
847 INTO v_avg_rating
848 FROM review r
849 JOIN plan_item pi ON r.plan_item_id = pi.plan_item_id
850 WHERE pi.place_id = v_place_id;
851
852 UPDATE place
853 SET rating = v_avg_rating
854 WHERE place_id = v_place_id;
855
856 RAISE NOTICE 'Place % rating updated to %.', v_place_id, v_avg_rating;
857
858 RETURN COALESCE(NEW, OLD);
859END;
860$$;
861
862
863
864-- DROP стар trigger ако постои
865
866DROP TRIGGER IF EXISTS trg_update_place_rating_after_review ON review;
867
868
869
870-- CREATE TRIGGER
871
872
873CREATE TRIGGER trg_update_place_rating_after_review
874AFTER INSERT OR UPDATE OR DELETE ON review
875FOR EACH ROW
876EXECUTE FUNCTION fn_update_place_rating_after_review();
877
878
879-- test
880
881SELECT
882 pi.plan_item_id,
883 pi.place_id,
884 p.name,
885 p.rating
886FROM plan_item pi
887JOIN place p ON pi.place_id = p.place_id
888ORDER BY pi.plan_item_id
889LIMIT 10;