| 251 | | CREATE OR REPLACE FUNCTION test_stock_volatility() |
| 252 | | RETURNS void AS $$ |
| 253 | | DECLARE |
| 254 | | start_time timestamptz; |
| 255 | | end_time timestamptz; |
| 256 | | duration int; |
| 257 | | BEGIN |
| 258 | | start_time := clock_timestamp(); |
| 259 | | |
| 260 | | PERFORM |
| 261 | | sh.stock_id, |
| 262 | | sh.price, |
| 263 | | (sh.price - LAG(sh.price) OVER ( |
| 264 | | PARTITION BY sh.stock_id ORDER BY sh.timestamp)) |
| 265 | | / NULLIF(LAG(sh.price) OVER ( |
| 266 | | PARTITION BY sh.stock_id ORDER BY sh.timestamp), 0) AS daily_return |
| 267 | | FROM stock_history sh |
| 268 | | WHERE sh.timestamp >= '2024-01-01' |
| 269 | | AND sh.timestamp <= '2024-12-31'; |
| 270 | | |
| 271 | | end_time := clock_timestamp(); |
| 272 | | duration := round(1000 * (extract(epoch FROM end_time) |
| 273 | | - extract(epoch FROM start_time))); |
| 274 | | RAISE NOTICE 'Query executed in: % ms', duration; |
| 275 | | END; |
| 276 | | $$ LANGUAGE plpgsql; |
| 277 | | |
| 284 | | -- run 1: no index |
| 285 | | SELECT test_stock_volatility(); |
| 286 | | |
| 287 | | -- run 2: + idx_sh_stock_id_timestamp (composite) |
| 288 | | CREATE INDEX idx_sh_stock_id_timestamp ON stock_history(stock_id, timestamp); |
| 289 | | ANALYZE stock_history; |
| 290 | | SELECT test_stock_volatility(); |
| 291 | | |
| 292 | | -- run 3: + idx_tx_stock_id_timestamp (composite) |
| 293 | | CREATE INDEX idx_tx_stock_id_timestamp ON transactions(stock_id, timestamp); |
| | 257 | EXPLAIN ANALYZE |
| | 258 | SELECT |
| | 259 | sh.stock_id, |
| | 260 | sh.price, |
| | 261 | (sh.price - LAG(sh.price) OVER ( |
| | 262 | PARTITION BY sh.stock_id ORDER BY sh.timestamp)) |
| | 263 | / NULLIF(LAG(sh.price) OVER ( |
| | 264 | PARTITION BY sh.stock_id ORDER BY sh.timestamp), 0) AS daily_return |
| | 265 | FROM stock_history sh |
| | 266 | WHERE sh.timestamp >= '2024-01-01' |
| | 267 | AND sh.timestamp <= '2024-12-31'; |
| | 268 | |
| | 269 | QUERY PLAN | |
| | 270 | ---------------------------------------------------------------------------------------------------------------------------------+ |
| | 271 | WindowAgg (cost=8773.08..9784.81 rows=36791 width=28) (actual time=41.592..66.876 rows=36600 loops=1) | |
| | 272 | -> Sort (cost=8773.06..8865.03 rows=36791 width=20) (actual time=41.543..46.298 rows=36600 loops=1) | |
| | 273 | Sort Key: stock_id, "timestamp" | |
| | 274 | Sort Method: external merge Disk: 1256kB | |
| | 275 | -> Seq Scan on stock_history sh (cost=0.00..4471.00 rows=36791 width=20) (actual time=0.060..22.078 rows=36600 loops=1)| |
| | 276 | Filter: (("timestamp" >= '2024-01-01'::date) AND ("timestamp" <= '2024-12-31'::date)) | |
| | 277 | Rows Removed by Filter: 163400 | |
| | 278 | Planning Time: 0.526 ms | |
| | 279 | Execution Time: 69.235 ms | |
| | 280 | |
| | 281 | }}} |
| | 282 | |
| | 283 | |
| | 284 | |
| | 285 | '''Индекси:''' |
| | 286 | |
| | 287 | |
| | 288 | --- |
| | 289 | |
| | 290 | **1. transactions - Composite Index (stock_id, timestamp)** |
| | 291 | {{{ |
| | 292 | CREATE INDEX idx_tx_stock_id_timestamp |
| | 293 | ON transactions(stock_id, timestamp); |
| | 294 | |
| 295 | | SELECT test_stock_volatility(); |
| 296 | | |
| 297 | | -- run 4: + idx_watchlist_stock_id |
| | 296 | EXPLAIN ANALYZE |
| | 297 | SELECT ... |
| | 298 | |
| | 299 | |
| | 300 | QUERY PLAN | |
| | 301 | -------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| | 302 | WindowAgg (cost=0.66..9098.75 rows=36545 width=28) (actual time=0.078..41.712 rows=36600 loops=1) | |
| | 303 | -> Index Scan using idx_sh_stock_id_timestamp on stock_history sh (cost=0.42..8185.12 rows=36545 width=20) (actual time=0.066..17.591 rows=36600 loops=1)| |
| | 304 | Index Cond: (("timestamp" >= '2024-01-01'::date) AND ("timestamp" <= '2024-12-31'::date)) | |
| | 305 | Planning Time: 0.163 ms | |
| | 306 | Execution Time: 43.629 ms | |
| | 307 | |
| | 308 | }}} |
| | 309 | |
| | 310 | |
| | 311 | --- |
| | 312 | |
| | 313 | **2. watchlist - Index (stock_id)** |
| | 314 | {{{ |
| 300 | | SELECT test_stock_volatility(); |
| 301 | | |
| 302 | | -- run 5: + idx_tr_symbol_status (composite) |
| 303 | | CREATE INDEX idx_tr_symbol_status ON trade_request(stock_symbol, status); |
| 304 | | ANALYZE trade_request; |
| 305 | | SELECT test_stock_volatility(); |
| 306 | | |
| 307 | | DROP FUNCTION test_stock_volatility(); |
| 308 | | }}} |
| 309 | | |
| 310 | | |
| 311 | | |
| 312 | | '''Индекси:''' |
| 313 | | |
| 314 | | **1. stock_history - Composite Index (stock_id, timestamp)** |
| 315 | | {{{ |
| 316 | | CREATE INDEX idx_sh_stock_id_timestamp |
| 317 | | ON stock_history(stock_id, timestamp); |
| 318 | | }}} |
| 319 | | Користење: `PARTITION BY sh.stock_id ORDER BY sh.timestamp` во LAG прозорската функција и WHERE филтерот за дати |
| 320 | | |
| 321 | | Подобрување: Composite индексот ги покрива и партиционирањето и сортирањето -= елиминира Sort чекор |
| 322 | | |
| 323 | | --- |
| 324 | | |
| 325 | | **2. transactions - Composite Index (stock_id, timestamp)** |
| 326 | | {{{ |
| 327 | | CREATE INDEX idx_tx_stock_id_timestamp |
| 328 | | ON transactions(stock_id, timestamp); |
| 329 | | }}} |
| 330 | | Користење: GROUP BY `t.stock_id` и WHERE на `timestamp` во `txn_stats` |
| 331 | | |
| 332 | | Подобрување: Директен Index Scan наместо Seq Scan + Sort при временски филтрирани агрегации |
| 333 | | |
| 334 | | --- |
| 335 | | |
| 336 | | **3. watchlist - Index (stock_id)** |
| 337 | | {{{ |
| 338 | | CREATE INDEX idx_watchlist_stock_id |
| 339 | | ON watchlist(stock_id); |
| 340 | | }}} |
| 341 | | Користење: GROUP BY `stock_id` во `watchlist_stats` |
| 342 | | |
| 343 | | Подобрување: Index Scan при агрегација по акција наместо Seq Scan |
| 344 | | |
| 345 | | --- |
| 346 | | |
| 347 | | **4. trade_request - Composite Index (stock_symbol, status)** |
| 348 | | {{{ |
| 349 | | CREATE INDEX idx_tr_symbol_status |
| 350 | | ON trade_request(stock_symbol, status); |
| 351 | | }}} |
| 352 | | Користење: JOIN `tr.stock_symbol = s.symbol` и WHERE `tr.status = 'PENDING'` во `pending_stats` |
| 353 | | |
| 354 | | Подобрување: Composite индексот ги покрива и JOIN условот и WHERE филтерот истовремено |
| 355 | | |
| 356 | | --- |
| | 317 | EXPLAIN ANALYZE |
| | 318 | SELECT ... |
| | 319 | |
| | 320 | QUERY PLAN | |
| | 321 | -------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| | 322 | WindowAgg (cost=0.66..9098.75 rows=36545 width=28) (actual time=0.083..42.003 rows=36600 loops=1) | |
| | 323 | -> Index Scan using idx_sh_stock_id_timestamp on stock_history sh (cost=0.42..8185.12 rows=36545 width=20) (actual time=0.070..17.564 rows=36600 loops=1)| |
| | 324 | Index Cond: (("timestamp" >= '2024-01-01'::date) AND ("timestamp" <= '2024-12-31'::date)) | |
| | 325 | Planning Time: 0.162 ms | |
| | 326 | Execution Time: 43.946 ms | |
| | 327 | }}} |
| | 328 | |
| | 329 | |