| 11 | | |
| 12 | | {{{ |
| 13 | | SELECT |
| 14 | | fb.user_id, |
| 15 | | fb.username, |
| 16 | | fb.email, |
| 17 | | COALESCE(ai.income_count, 0) AS income_count, |
| 18 | | COALESCE(ai.annual_total_income, 0) AS annual_total_income, |
| 19 | | COALESCE(ma.active_months, 0) AS active_months, |
| 20 | | ROUND((COALESCE(ma.active_months, 0) / 12.0)::numeric, 4) AS activity_ratio, |
| 21 | | ROUND((COALESCE(fb.spending_budget, 0) / 100.0) * COALESCE(ai.annual_total_income, 0), 2) AS spending_amount, |
| 22 | | ROUND((COALESCE(fb.saving_budget, 0) / 100.0) * COALESCE(ai.annual_total_income, 0), 2) AS saving_amount, |
| 23 | | ROUND((COALESCE(fb.investing_budget, 0) / 100.0) * COALESCE(ai.annual_total_income, 0), 2) AS investing_amount, |
| 24 | | ROUND((COALESCE(fb.donation_budget, 0) / 100.0) * COALESCE(ai.annual_total_income, 0), 2) AS donation_amount, |
| 25 | | ROUND((COALESCE(fb.credit, 0) / 100.0) * COALESCE(ai.annual_total_income, 0), 2) AS credit_amount |
| 26 | | FROM finance_users fb |
| 27 | | LEFT JOIN ( |
| 28 | | SELECT user_id, COUNT(*) AS income_count, SUM(amount) AS annual_total_income |
| 29 | | FROM incomes |
| 30 | | WHERE EXTRACT(YEAR FROM date) = 2026 |
| 31 | | GROUP BY user_id |
| 32 | | ) ai ON ai.user_id = fb.user_id |
| 33 | | LEFT JOIN ( |
| 34 | | SELECT user_id, COUNT(DISTINCT EXTRACT(MONTH FROM date)) AS active_months |
| 35 | | FROM incomes |
| 36 | | WHERE EXTRACT(YEAR FROM date) = 2026 |
| 37 | | GROUP BY user_id |
| 38 | | ) ma ON ma.user_id = fb.user_id |
| 39 | | ORDER BY annual_total_income DESC; |
| 40 | | }}} |
| 41 | | Индекси: |
| 42 | | |
| 43 | | 1. |
| 44 | | |
| 45 | | {{{ |
| 46 | | idx_incomes_user_date |
| 47 | | CREATE INDEX idx_incomes_user_date ON incomes(user_id, date); |
| 48 | | }}} |
| 49 | | |
| 50 | | Подобрување: |
| 51 | | Index Scan наместо Seq Scan + подобро филтрирање по година |
| 52 | | |
| 53 | | 2. |
| 54 | | {{{ |
| 55 | | idx_incomes_year |
| 56 | | CREATE INDEX idx_incomes_year ON incomes ((EXTRACT(YEAR FROM date))); |
| 57 | | }}} |
| 58 | | |
| 59 | | Подобрување: |
| 60 | | Избегнува full table scan за годишен филтер |
| 61 | | |
| 62 | | 3. |
| 63 | | {{{ |
| 64 | | |
| 65 | | idx_incomes_covering |
| 66 | | CREATE INDEX idx_incomes_covering ON incomes(user_id, date, amount); |
| 67 | | |
| 68 | | }}} |
| 69 | | |
| 70 | | Подобрување: |
| 71 | | Direct lookup |
| 72 | | |
| 73 | | |
| 74 | | 4. |
| 75 | | {{{ |
| 76 | | CREATE UNIQUE INDEX users_pkey |
| 77 | | ON users(user_id); |
| 78 | | |
| 79 | | }}} |
| 80 | | |
| 81 | | Подобрување: |
| 82 | | Fast join |
| 83 | | |
| 84 | | 5. |
| 85 | | {{{ |
| 86 | | CREATE INDEX idx_incomes_covering |
| 87 | | ON incomes(user_id, date, amount); |
| 88 | | |
| 89 | | }}} |
| 90 | | |
| 91 | | Подобрување: |
| 92 | | Index-only scan (без пристап до табелата) |
| 93 | | |
| 94 | | ==SQL за анализа на брзината |
| 110 | | COALESCE(ai.income_count, 0), |
| 111 | | COALESCE(ai.annual_total_income, 0), |
| 112 | | COALESCE(ma.active_months, 0) |
| | 26 | COALESCE(stats.income_count, 0), |
| | 27 | COALESCE(stats.annual_total_income, 0), |
| | 28 | COALESCE(stats.active_months, 0) |
| 119 | | ) ai ON ai.user_id = fb.user_id |
| | 40 | ) stats ON stats.user_id = fb.user_id |
| | 41 | ) t; |
| | 42 | |
| | 43 | end_time := clock_timestamp(); |
| | 44 | duration := round(1000 * (extract(epoch FROM end_time) - extract(epoch FROM start_time))); |
| | 45 | |
| | 46 | RAISE NOTICE 'Query executed in: % ms', duration; |
| | 47 | END; |
| | 48 | |
| | 49 | }}} |
| | 50 | Индекси: |
| | 51 | |
| | 52 | 1. |
| | 53 | |
| | 54 | {{{ |
| | 55 | idx_incomes_user_date |
| | 56 | CREATE INDEX idx_incomes_user_date ON incomes(user_id, date); |
| | 57 | }}} |
| | 58 | |
| | 59 | Подобрување: |
| | 60 | Index Scan наместо Seq Scan + подобро филтрирање по година |
| | 61 | |
| | 62 | 2. |
| | 63 | {{{ |
| | 64 | idx_incomes_year |
| | 65 | CREATE INDEX idx_incomes_year ON incomes ((EXTRACT(YEAR FROM date))); |
| | 66 | }}} |
| | 67 | |
| | 68 | Подобрување: |
| | 69 | Избегнува full table scan за годишен филтер |
| | 70 | |
| | 71 | 3. |
| | 72 | {{{ |
| | 73 | |
| | 74 | idx_incomes_covering |
| | 75 | CREATE INDEX idx_incomes_covering ON incomes(user_id, date, amount); |
| | 76 | |
| | 77 | }}} |
| | 78 | |
| | 79 | Подобрување: |
| | 80 | Direct lookup |
| | 81 | |
| | 82 | |
| | 83 | 4. |
| | 84 | {{{ |
| | 85 | CREATE UNIQUE INDEX users_pkey |
| | 86 | ON users(user_id); |
| | 87 | |
| | 88 | }}} |
| | 89 | |
| | 90 | Подобрување: |
| | 91 | Fast join |
| | 92 | |
| | 93 | 5. |
| | 94 | {{{ |
| | 95 | CREATE INDEX idx_incomes_covering |
| | 96 | ON incomes(user_id, date, amount); |
| | 97 | |
| | 98 | }}} |
| | 99 | |
| | 100 | Подобрување: |
| | 101 | Index-only scan (без пристап до табелата) |
| | 102 | |
| | 103 | ==SQL за анализа на брзината |
| | 104 | |
| | 105 | {{{ |
| | 106 | CREATE OR REPLACE FUNCTION test_finance_report() |
| | 107 | RETURNS void AS $$ |
| | 108 | DECLARE |
| | 109 | start_time timestamptz; |
| | 110 | end_time timestamptz; |
| | 111 | duration int; |
| | 112 | BEGIN |
| | 113 | start_time := clock_timestamp(); |
| | 114 | |
| | 115 | PERFORM * |
| | 116 | FROM ( |
| | 117 | SELECT |
| | 118 | fb.user_id, |
| | 119 | COALESCE(stats.income_count, 0), |
| | 120 | COALESCE(stats.annual_total_income, 0), |
| | 121 | COALESCE(stats.active_months, 0) |
| | 122 | FROM finance_users fb |