Changes between Version 1 and Version 2 of otherdevelopment


Ignore:
Timestamp:
03/02/26 00:48:37 (2 weeks ago)
Author:
231020
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • otherdevelopment

    v1 v2  
    33== Анализа на перформанси ==
    44
    5 
     5=== Извештај за диверзификација на портфолио (report_portfolio_diversification) ===
     6
     7Анализата на перформанси се врши врз основа на моменталната состојба во базата.
     8
     9'''SQL:'''
     10{{{
     11-- Core query inside report_portfolio_diversification()
     12WITH holding_values AS (
     13    SELECT ph.portfolio_id AS hv_portfolio_id,
     14           ph.stock_id     AS hv_stock_id,
     15           s.symbol        AS hv_symbol,
     16           ph.quantity::NUMERIC * s.current_price::NUMERIC                           AS market_value,
     17           ph.quantity::NUMERIC * (s.current_price::NUMERIC - ph.avg_price::NUMERIC) AS unrealized_pnl
     18    FROM portfolio_holdings ph
     19    JOIN stock s ON ph.stock_id = s.id
     20    WHERE ph.quantity > 0
     21),
     22stock_weights AS (
     23    SELECT hv.hv_portfolio_id,
     24           hv.hv_stock_id,
     25           hv.hv_symbol,
     26           SUM(hv.market_value)   AS stock_market_value,
     27           SUM(hv.unrealized_pnl) AS stock_unrealized_pnl,
     28           COUNT(*)               AS lots_count
     29    FROM holding_values hv
     30    GROUP BY hv.hv_portfolio_id, hv.hv_stock_id, hv.hv_symbol
     31),
     32portfolio_totals AS (
     33    SELECT sw.hv_portfolio_id,
     34           SUM(sw.stock_market_value)    AS total_holdings_value,
     35           SUM(sw.stock_unrealized_pnl)  AS total_unrealized_pnl,
     36           COUNT(DISTINCT sw.hv_stock_id) AS num_stocks,
     37           SUM(sw.lots_count)            AS total_lots
     38    FROM stock_weights sw
     39    GROUP BY sw.hv_portfolio_id
     40)
     41SELECT u.id, u.username, p.id, p.balance, ...
     42FROM users u
     43JOIN portfolios p ON u.id = p.user_id
     44LEFT JOIN portfolio_totals pt  ON p.id = pt.hv_portfolio_id
     45WHERE u.role = 'USER'
     46ORDER BY hhi_score ASC, total_portfolio_value DESC;
     47}}}
     48
     49
     50'''EXPLAIN ANALYZE без индекси:'''
     51{{{
     52EXPLAIN ANALYZE
     53SELECT ph.portfolio_id, ph.stock_id, ph.quantity, s.current_price, s.symbol
     54FROM portfolio_holdings ph
     55JOIN stock s ON ph.stock_id = s.id
     56WHERE ph.quantity > 0;
     57
     58-- резултат:
     59Hash Join  (cost=11.57..34.17 rows=313 width=544) (actual time=0.105..0.116 rows=4 loops=1)
     60  Hash Cond: (ph.stock_id = s.id)
     61  ->  Seq Scan on portfolio_holdings ph  (cost=0.00..21.75 rows=313 width=20) (actual time=0.034..0.042 rows=4 loops=1)
     62        Filter: (quantity > 0)
     63  ->  Hash  (cost=10.70..10.70 rows=70 width=532) (actual time=0.036..0.037 rows=4 loops=1)
     64        Buckets: 1024  Batches: 1  Memory Usage: 9kB
     65        ->  Seq Scan on stock s  (cost=0.00..10.70 rows=70 width=532) (actual time=0.019..0.020 rows=4 loops=1)
     66Planning Time: 1.315 ms
     67Execution Time: 0.180 ms
     68}}}
     69
     70'''Индекси:'''
     71
     72**1. portfolio_holdings - Index (portfolio_id)**
     73{{{
     74CREATE INDEX idx_ph_portfolio_id
     75ON portfolio_holdings(portfolio_id);
     76}}}
     77Користење: GROUP BY и JOIN по `portfolio_id` во CTE-ата `stock_weights` и `portfolio_totals`
     78
     79Подобрување: Index Scan наместо Seq Scan при групирање по портфолио
     80
     81---
     82
     83**2. portfolio_holdings - Index (stock_id)**
     84{{{
     85CREATE INDEX idx_ph_stock_id
     86ON portfolio_holdings(stock_id);
     87}}}
     88Користење: JOIN `ph.stock_id = s.id` во `holding_values` CTE
     89
     90Подобрување: Директен lookup наместо Hash Join со Seq Scan
     91
     92---
     93
     94**3. portfolio_holdings - Partial Index (quantity > 0)**
     95{{{
     96CREATE INDEX idx_ph_quantity_positive
     97ON portfolio_holdings(portfolio_id, stock_id)
     98WHERE quantity > 0;
     99}}}
     100Користење: WHERE филтер `ph.quantity > 0` во `holding_values` CTE
     101
     102Подобрување: Индексот ги содржи само редовите со quantity > 0, елиминирајќи ги нулираните холдинзи уште при скенирањето
     103
     104---
     105
     106**4. users - Index (role)**
     107{{{
     108CREATE INDEX idx_users_role
     109ON users(role);
     110}}}
     111Користење: WHERE филтер `u.role = 'USER'` во финалниот SELECT
     112
     113Подобрување: Директен Index Scan наместо Seq Scan на целата users табела
     114
     115---
     116
     117'''EXPLAIN ANALYZE со индекси:'''
     118{{{
     119EXPLAIN ANALYZE
     120SELECT ph.portfolio_id, ph.stock_id, ph.quantity, s.current_price, s.symbol
     121FROM portfolio_holdings ph
     122JOIN stock s ON ph.stock_id = s.id
     123WHERE ph.quantity > 0;
     124
     125--  резултат:
     126Nested Loop  (cost=0.14..9.44 rows=1 width=544) (actual time=0.034..0.042 rows=4 loops=1)
     127  ->  Seq Scan on portfolio_holdings ph  (cost=0.00..1.05 rows=1 width=20) (actual time=0.015..0.017 rows=4 loops=1)
     128        Filter: (quantity > 0)
     129  ->  Index Scan using stock_pkey on stock s  (cost=0.14..8.16 rows=1 width=532) (actual time=0.005..0.005 rows=1 loops=4)
     130        Index Cond: (id = ph.stock_id)
     131Planning Time: 0.724 ms
     132Execution Time: 0.086 ms
     133}}}
     134
     135'''Заклучок:'''
     136Имаме забрзување.
     137
     138
     139----
     140=== Извештај за волатилност на акции (report_stock_volatility_and_activity) ===
     141'''SQL:'''
     142{{{
     143-- Core query inside report_stock_volatility_and_activity()
     144WITH daily_returns AS (
     145    SELECT sh.stock_id AS dr_stock_id,
     146           sh.price    AS dr_price,
     147           (sh.price - LAG(sh.price) OVER (
     148               PARTITION BY sh.stock_id ORDER BY sh.timestamp))
     149           / NULLIF(LAG(sh.price) OVER (
     150               PARTITION BY sh.stock_id ORDER BY sh.timestamp), 0)
     151           AS dr_daily_return
     152    FROM stock_history sh
     153    WHERE sh.timestamp >= p_from_date
     154      AND sh.timestamp <= p_to_date
     155),
     156price_stats AS (
     157    SELECT dr.dr_stock_id,
     158           COUNT(*)  AS ps_total_points,
     159           MIN(dr.dr_price) AS ps_min_price,
     160           MAX(dr.dr_price) AS ps_max_price,
     161           AVG(dr.dr_price) AS ps_avg_price,
     162           COALESCE(STDDEV(dr.dr_daily_return) * 100, 0) AS ps_volatility_pct
     163    FROM daily_returns dr
     164    GROUP BY dr.dr_stock_id
     165),
     166txn_stats AS (
     167    SELECT t.stock_id AS ts_stock_id,
     168           SUM(t.price * t.quantity)                 AS ts_total_volume,
     169           COUNT(*)                                  AS ts_total_count,
     170           COUNT(*) FILTER (WHERE t.type = 'BUY')   AS ts_buy_count,
     171           COUNT(*) FILTER (WHERE t.type = 'SELL')  AS ts_sell_count
     172    FROM transactions t
     173    WHERE t.timestamp >= p_from_date
     174      AND t.timestamp <= p_to_date
     175    GROUP BY t.stock_id
     176)
     177SELECT s.id, s.symbol, s.name, s.current_price, ...
     178FROM stock s
     179LEFT JOIN price_stats ps ON s.id = ps.dr_stock_id
     180LEFT JOIN txn_stats   ts ON s.id = ts.ts_stock_id
     181ORDER BY historical_volatility_pct DESC, activity_score DESC;
     182}}}
     183
     184
     185
     186'''EXPLAIN ANALYZE без индекси:'''
     187{{{
     188EXPLAIN ANALYZE
     189SELECT sh.stock_id, sh.price, sh.timestamp
     190FROM stock_history sh
     191WHERE sh.timestamp >= '2024-01-01'
     192  AND sh.timestamp <= '2024-12-31'
     193ORDER BY sh.stock_id, sh.timestamp;
     194
     195-- резултат:
     196Sort  (cost=31.85..31.87 rows=7 width=20) (actual time=0.051..0.052 rows=0 loops=1)
     197  Sort Key: stock_id, "timestamp"
     198  Sort Method: quicksort  Memory: 25kB
     199  ->  Seq Scan on stock_history sh  (cost=0.00..31.75 rows=7 width=20) (actual time=0.018..0.018 rows=0 loops=1)
     200        Filter: (("timestamp" >= '2024-01-01'::date) AND ("timestamp" <= '2024-12-31'::date))
     201        Rows Removed by Filter: 8
     202Planning Time: 0.417 ms
     203Execution Time: 0.077 ms
     204}}}
     205
     206
     207'''Индекси:'''
     208
     209**1. stock_history - Composite Index (stock_id, timestamp)**
     210{{{
     211CREATE INDEX idx_sh_stock_id_timestamp
     212ON stock_history(stock_id, timestamp);
     213}}}
     214Користење: `PARTITION BY sh.stock_id ORDER BY sh.timestamp` во LAG прозорската функција и WHERE филтерот за дати
     215
     216Подобрување: Composite индексот ги покрива и партиционирањето и сортирањето -= елиминира Sort чекор
     217
     218---
     219
     220**2. transactions - Composite Index (stock_id, timestamp)**
     221{{{
     222CREATE INDEX idx_tx_stock_id_timestamp
     223ON transactions(stock_id, timestamp);
     224}}}
     225Користење: GROUP BY `t.stock_id` и WHERE на `timestamp` во `txn_stats`
     226
     227Подобрување: Директен Index Scan наместо Seq Scan + Sort при временски филтрирани агрегации
     228
     229---
     230
     231**3. watchlist - Index (stock_id)**
     232{{{
     233CREATE INDEX idx_watchlist_stock_id
     234ON watchlist(stock_id);
     235}}}
     236Користење: GROUP BY `stock_id` во `watchlist_stats`
     237
     238Подобрување: Index Scan при агрегација по акција наместо Seq Scan
     239
     240---
     241
     242**4. trade_request - Composite Index (stock_symbol, status)**
     243{{{
     244CREATE INDEX idx_tr_symbol_status
     245ON trade_request(stock_symbol, status);
     246}}}
     247Користење: JOIN `tr.stock_symbol = s.symbol` и WHERE `tr.status = 'PENDING'` во `pending_stats`
     248
     249Подобрување: Composite индексот ги покрива и JOIN условот и WHERE филтерот истовремено
     250
     251---
     252
     253'''EXPLAIN ANALYZE со индекси:'''
     254{{{
     255EXPLAIN ANALYZE
     256SELECT sh.stock_id, sh.price, sh.timestamp
     257FROM stock_history sh
     258WHERE sh.timestamp >= '2024-01-01'
     259  AND sh.timestamp <= '2024-12-31'
     260ORDER BY sh.stock_id, sh.timestamp;
     261
     262-- резултат:
     263Sort  (cost=1.13..1.14 rows=1 width=20) (actual time=0.024..0.025 rows=0 loops=1)
     264  Sort Key: stock_id, "timestamp"
     265  Sort Method: quicksort  Memory: 25kB
     266  ->  Seq Scan on stock_history sh  (cost=0.00..1.12 rows=1 width=20) (actual time=0.017..0.017 rows=0 loops=1)
     267        Filter: (("timestamp" >= '2024-01-01'::date) AND ("timestamp" <= '2024-12-31'::date))
     268        Rows Removed by Filter: 8
     269Planning Time: 0.397 ms
     270Execution Time: 0.054 ms
     271}}}
     272
     273'''Заклучок:'''
     274Имаме забрзување.
    6275----
    7276