AdvancedTopics: AdvancedMap.sql

File AdvancedMap.sql, 10.5 KB (added by 231151, 8 days ago)
Line 
1-- димензија за време
2CREATE TABLE DIM_TIME AS
3SELECT
4 d::DATE AS date_key,
5 EXTRACT(YEAR FROM d)::INT AS year,
6 EXTRACT(MONTH FROM d)::INT AS month,
7 TO_CHAR(d, 'Month') AS month_name,
8 EXTRACT(QUARTER FROM d)::INT AS quarter,
9 EXTRACT(HOUR FROM d) AS hour_of_day,
10 CASE
11 WHEN EXTRACT(DOW FROM d) IN (0,6) THEN 'WEEKEND'
12 ELSE 'WORKDAY'
13 END AS day_type,
14 CASE
15 WHEN EXTRACT(HOUR FROM d) BETWEEN 7 AND 9 THEN 'MORNING_PEAK'
16 WHEN EXTRACT(HOUR FROM d) BETWEEN 16 AND 18 THEN 'EVENING_PEAK'
17 WHEN EXTRACT(HOUR FROM d) BETWEEN 10 AND 15 THEN 'MIDDAY'
18 WHEN EXTRACT(HOUR FROM d) BETWEEN 19 AND 22 THEN 'EVENING'
19 ELSE 'OFF_PEAK'
20 END AS time_slot
21FROM GENERATE_SERIES('2024-01-01'::DATE, '2026-12-31'::DATE, '1 hour'::INTERVAL) d;
22
23-- централна факт табела за cube аналитика
24CREATE TABLE FACT_TRANSPORT AS
25SELECT
26 t.TRIP_ID,
27 t.ROUTE_ID,
28 r.ROUTE_NAME,
29 t.VEHICLE_ID,
30 t.DRIVER_ID,
31 t.TRIP_DATE,
32 EXTRACT(MONTH FROM t.TRIP_DATE)::INT AS month,
33 EXTRACT(YEAR FROM t.TRIP_DATE)::INT AS year,
34 EXTRACT(HOUR FROM t.START_TIME)::INT AS hour_of_day,
35 CASE
36 WHEN EXTRACT(DOW FROM t.TRIP_DATE) IN (0,6) THEN 'WEEKEND'
37 ELSE 'WORKDAY'
38 END AS day_type,
39 CASE
40 WHEN EXTRACT(HOUR FROM t.START_TIME) BETWEEN 7 AND 9 THEN 'MORNING_PEAK'
41 WHEN EXTRACT(HOUR FROM t.START_TIME) BETWEEN 16 AND 18 THEN 'EVENING_PEAK'
42 WHEN EXTRACT(HOUR FROM t.START_TIME) BETWEEN 10 AND 15 THEN 'MIDDAY'
43 WHEN EXTRACT(HOUR FROM t.START_TIME) BETWEEN 19 AND 22 THEN 'EVENING'
44 ELSE 'OFF_PEAK'
45 END AS time_slot,
46 t.STATUS AS trip_status,
47 COUNT(tk.TICKET_ID) AS passengers,
48 SUM(tk.PRICE) AS revenue,
49 COALESCE(SUM(dl.DELAY_MINUTES), 0) AS total_delay_min,
50 COALESCE(AVG(dl.DELAY_MINUTES), 0) AS avg_delay_min,
51 COALESCE(AVG(cl.PASSENGER_COUNT * 100.0
52 / NULLIF(v.CAPACITY, 0)), 0) AS avg_occupancy_pct,
53 COALESCE(MAX(cl.PASSENGER_COUNT * 100.0
54 / NULLIF(v.CAPACITY, 0)), 0) AS max_occupancy_pct
55FROM TRIP t
56JOIN ROUTE r ON t.ROUTE_ID = r.ROUTE_ID
57JOIN VEHICLE v ON t.VEHICLE_ID = v.VEHICLE_ID
58LEFT JOIN TICKET tk ON t.TRIP_ID = tk.TRIP_ID
59 AND tk.STATUS NOT IN ('CANCELLED', 'REFUNDED')
60LEFT JOIN DELAY_LOG dl ON t.TRIP_ID = dl.TRIP_ID
61LEFT JOIN CAPACITY_LOG cl ON t.TRIP_ID = cl.TRIP_ID
62GROUP BY
63 t.TRIP_ID, t.ROUTE_ID, r.ROUTE_NAME, t.VEHICLE_ID,
64 t.DRIVER_ID, t.TRIP_DATE, t.START_TIME, t.STATUS, v.CAPACITY;
65-- главен datacube со сите комбинации
66CREATE MATERIALIZED VIEW MV_TRANSPORT_CUBE AS
67SELECT
68 route_name,
69 month,
70 year,
71 hour_of_day,
72 day_type,
73 time_slot,
74 trip_status,
75 COUNT(trip_id) AS total_trips,
76 SUM(passengers) AS total_passengers,
77 SUM(revenue) AS total_revenue,
78 AVG(avg_delay_min) AS avg_delay_min,
79 MAX(total_delay_min) AS max_delay_min,
80 AVG(avg_occupancy_pct) AS avg_occupancy_pct,
81 MAX(max_occupancy_pct) AS max_occupancy_pct,
82 COUNT(*) FILTER (
83 WHERE avg_occupancy_pct > 85
84 ) AS overcrowded_trips,
85 COUNT(*) FILTER (
86 WHERE avg_delay_min > 10
87 ) AS delayed_trips
88FROM FACT_TRANSPORT
89GROUP BY CUBE(
90 route_name,
91 month,
92 year,
93 hour_of_day,
94 day_type,
95 time_slot,
96 trip_status
97);
98
99-- детекција на системско доцнење по линија и час
100CREATE MATERIALIZED VIEW MV_SCHEDULE_MISMATCH AS
101SELECT
102 route_name,
103 hour_of_day,
104 day_type,
105 COUNT(trip_id) AS total_trips,
106 AVG(avg_delay_min) AS avg_delay,
107 MAX(total_delay_min) AS max_delay,
108 COUNT(*) FILTER (
109 WHERE avg_delay_min > 10
110 ) AS chronic_delay_trips,
111 ROUND(COUNT(*) FILTER (
112 WHERE avg_delay_min > 10
113 ) * 100.0 / NULLIF(COUNT(*), 0), 2) AS chronic_delay_pct,
114 CASE
115 WHEN AVG(avg_delay_min) > 15 THEN 'КРИТИЧНО — распоредот не соодветствува'
116 WHEN AVG(avg_delay_min) > 10 THEN 'ПРОБЛЕМАТИЧНО — потребна корекција'
117 WHEN AVG(avg_delay_min) > 5 THEN 'УМЕРЕНО — следење потребно'
118 ELSE 'ОК'
119 END AS schedule_status
120FROM FACT_TRANSPORT
121GROUP BY ROLLUP(route_name, hour_of_day, day_type);
122-- детекција на преполни линии
123CREATE MATERIALIZED VIEW MV_OVERCROWDING_ANALYSIS AS
124SELECT
125 route_name,
126 month,
127 hour_of_day,
128 day_type,
129 time_slot,
130 AVG(avg_occupancy_pct) AS avg_occupancy,
131 MAX(max_occupancy_pct) AS peak_occupancy,
132 COUNT(trip_id) AS total_trips,
133 COUNT(*) FILTER (
134 WHERE avg_occupancy_pct > 85
135 ) AS overcrowded_trips,
136 COUNT(*) FILTER (
137 WHERE avg_occupancy_pct > 100
138 ) AS over_capacity_trips,
139 CASE
140 WHEN AVG(avg_occupancy_pct) > 90 THEN 'ИТНО — потребен почест бус'
141 WHEN AVG(avg_occupancy_pct) > 75 THEN 'ПРЕПОРАЧАНО — зголеми фреквенција'
142 WHEN AVG(avg_occupancy_pct) > 60 THEN 'СЛЕДИ — потенцијален проблем'
143 ELSE 'ОК'
144 END AS recommendation
145FROM FACT_TRANSPORT
146GROUP BY ROLLUP(route_name, month, hour_of_day, day_type, time_slot);
147
148-- побарувачка по постојка
149CREATE MATERIALIZED VIEW MV_STOP_DEMAND AS
150SELECT
151 s.STOP_ID,
152 s.STOP_NAME,
153 EXTRACT(MONTH FROM t.TRIP_DATE)::INT AS month,
154 EXTRACT(HOUR FROM st.DEPARTURE_TIME) AS hour_of_day,
155 CASE
156 WHEN EXTRACT(DOW FROM t.TRIP_DATE) IN (0,6) THEN 'WEEKEND'
157 ELSE 'WORKDAY'
158 END AS day_type,
159 COUNT(DISTINCT t.TRIP_ID) AS total_trips_through,
160 COUNT(tk.TICKET_ID) AS total_boardings,
161 AVG(cl.PASSENGER_COUNT) AS avg_passengers_at_stop,
162 CASE
163 WHEN COUNT(DISTINCT t.TRIP_ID) < 5
164 AND COUNT(tk.TICKET_ID) > 100
165 THEN 'ПОТРЕБНА НОВА ЛИНИЈА'
166 WHEN COUNT(DISTINCT t.TRIP_ID) BETWEEN 5 AND 10
167 AND AVG(cl.PASSENGER_COUNT) > 40
168 THEN 'ПОТРЕБНА ПОГОЛЕМА ФРЕКВЕНЦИЈА'
169 ELSE 'ОК'
170 END AS demand_status
171FROM STOPS s
172JOIN STOP_TIME st ON s.STOP_ID = st.STOP_ID
173JOIN TRIP t ON st.TRIP_ID = t.TRIP_ID
174LEFT JOIN TICKET tk ON t.TRIP_ID = tk.TRIP_ID
175LEFT JOIN CAPACITY_LOG cl ON t.TRIP_ID = cl.TRIP_ID
176GROUP BY CUBE(s.STOP_ID, s.STOP_NAME,
177 EXTRACT(MONTH FROM t.TRIP_DATE)::INT,
178 EXTRACT(HOUR FROM st.DEPARTURE_TIME),
179 day_type);
180
181
182
183-- ride shift детекција
184-- ако линија доцни дали патниците минуваат на друга
185CREATE MATERIALIZED VIEW MV_RIDE_SHIFT_DETECTION AS
186WITH delayed_trips AS (
187 SELECT
188 t.ROUTE_ID,
189 t.TRIP_DATE,
190 EXTRACT(HOUR FROM t.START_TIME)::INT AS hour_of_day,
191 AVG(dl.DELAY_MINUTES) AS avg_delay
192 FROM TRIP t
193 JOIN DELAY_LOG dl ON t.TRIP_ID = dl.TRIP_ID
194 GROUP BY t.ROUTE_ID, t.TRIP_DATE, EXTRACT(HOUR FROM t.START_TIME)::INT
195 HAVING AVG(dl.DELAY_MINUTES) > 10
196),
197normal_trips AS (
198 SELECT
199 t.ROUTE_ID,
200 t.TRIP_DATE,
201 EXTRACT(HOUR FROM t.START_TIME)::INT AS hour_of_day,
202 COUNT(tk.TICKET_ID) AS passengers
203 FROM TRIP t
204 LEFT JOIN TICKET tk ON t.TRIP_ID = tk.TRIP_ID
205 AND tk.STATUS NOT IN ('CANCELLED', 'REFUNDED')
206 GROUP BY t.ROUTE_ID, t.TRIP_DATE, EXTRACT(HOUR FROM t.START_TIME)::INT
207)
208SELECT
209 r.ROUTE_NAME,
210 n.TRIP_DATE,
211 n.hour_of_day,
212 d.avg_delay,
213 n.passengers AS actual_passengers,
214 AVG(n.passengers) OVER (
215 PARTITION BY n.ROUTE_ID, n.hour_of_day
216 ) AS avg_passengers_no_delay,
217 ROUND((AVG(n.passengers) OVER (
218 PARTITION BY n.ROUTE_ID, n.hour_of_day
219 ) - n.passengers) * 100.0 / NULLIF(
220 AVG(n.passengers) OVER (
221 PARTITION BY n.ROUTE_ID, n.hour_of_day
222 ), 0), 2) AS passenger_drop_pct,
223 CASE
224 WHEN (AVG(n.passengers) OVER (
225 PARTITION BY n.ROUTE_ID, n.hour_of_day
226 ) - n.passengers) > 20
227 THEN 'ВЕРОЈАТЕН RIDE SHIFT'
228 ELSE 'НОРМАЛНО'
229 END AS shift_status
230FROM normal_trips n
231JOIN delayed_trips d USING (ROUTE_ID, TRIP_DATE, hour_of_day)
232JOIN ROUTE r ON n.ROUTE_ID = r.ROUTE_ID;
233
234-- финален предлог за оптимизација
235CREATE MATERIALIZED VIEW MV_OPTIMIZATION_RECOMMENDATIONS AS
236SELECT
237 o.route_name,
238 o.hour_of_day,
239 o.day_type,
240 o.avg_occupancy,
241 o.recommendation AS occupancy_recommendation,
242 s.schedule_status AS delay_recommendation,
243 s.avg_delay,
244 CASE
245 WHEN o.avg_occupancy > 90
246 AND s.avg_delay > 10
247 THEN 'ИТНО — и преполна и доцни, потребна целосна ревизија'
248 WHEN o.avg_occupancy > 90
249 THEN 'Зголеми фреквенција на возења'
250 WHEN s.avg_delay > 10
251 THEN 'Корегирај распоред — не соодветствува со сообраќајот'
252 ELSE 'Без итни препораки'
253 END AS final_recommendation
254FROM MV_OVERCROWDING_ANALYSIS o
255JOIN MV_SCHEDULE_MISMATCH s
256 ON o.route_name = s.route_name
257 AND o.hour_of_day = s.hour_of_day
258 AND o.day_type = s.day_type
259WHERE o.avg_occupancy > 60 OR s.avg_delay > 5;