| 1 | = Индекси за оптимизација на напредни извештаи |
| 2 | |
| 3 | |
| 4 | == Анализа на извештаи и потребни индекси == #анализа |
| 5 | |
| 6 | === 1. Индекси за извештај за процент на изнајмени недвижности во однос на објавени огласи за секоја година.=== #извештај1 |
| 7 | |
| 8 | Овој извештај користи и извршува операции со: |
| 9 | * `EXTRACT(YEAR FROM available_from)` на Listing табела |
| 10 | * `EXTRACT(YEAR FROM l.start_date)` на Lease табела |
| 11 | * JOIN помеѓу Lease и Listing преку listing_id |
| 12 | |
| 13 | '''Потребни индекси:''' |
| 14 | |
| 15 | {{{#!sql |
| 16 | -- За брзо филтрирање по година на објавување |
| 17 | CREATE INDEX IF NOT EXISTS idx_listing_available_from_year ON Listing(EXTRACT(YEAR FROM available_from)); |
| 18 | |
| 19 | -- За брзо филтрирање по година на започнување на изнајмувањето |
| 20 | CREATE INDEX IF NOT EXISTS idx_lease_start_date_year ON Lease(EXTRACT(YEAR FROM start_date)); |
| 21 | |
| 22 | -- За оптимизација на JOIN операциите |
| 23 | CREATE INDEX IF NOT EXISTS idx_lease_listing_id ON Lease(listing_id); |
| 24 | |
| 25 | -- Композитен индекс за подобра оптимизација на временски операции |
| 26 | CREATE INDEX IF NOT EXISTS idx_listing_available_from ON Listing(available_from); |
| 27 | CREATE INDEX IF NOT EXISTS idx_lease_start_date ON Lease(start_date); |
| 28 | }}} |
| 29 | |
| 30 | === 2. Извештај за раст на приходи по град во однос на претходната година === #извештај2 |
| 31 | |
| 32 | Овој извештај е особено сложен бидејќи користи повеќе JOIN операции и агрегатни функции со временски филтрирања. |
| 33 | |
| 34 | '''Критични операции:''' |
| 35 | * JOIN низа: Payment → Lease → Listing → Unit → Property → Address |
| 36 | * WHERE услов: `p.status = 'завршено'` |
| 37 | * GROUP BY со `EXTRACT(YEAR FROM p.payment_date)` |
| 38 | |
| 39 | '''Потребни индекси:''' |
| 40 | |
| 41 | {{{#!sql |
| 42 | -- За брзо филтрирање на успешни плаќања |
| 43 | CREATE INDEX IF NOT EXISTS idx_payment_status ON Payment(status); |
| 44 | |
| 45 | -- За временски анализи на плаќања |
| 46 | CREATE INDEX IF NOT EXISTS idx_payment_date_year ON Payment(EXTRACT(YEAR FROM payment_date)); |
| 47 | CREATE INDEX IF NOT EXISTS idx_payment_date ON Payment(payment_date); |
| 48 | |
| 49 | -- За оптимизација на JOIN операциите |
| 50 | CREATE INDEX IF NOT EXISTS idx_payment_lease_id ON Payment(lease_id); |
| 51 | CREATE INDEX IF NOT EXISTS idx_listing_unit_id ON Listing(unit_id); |
| 52 | CREATE INDEX IF NOT EXISTS idx_unit_property_id ON Unit(property_id); |
| 53 | CREATE INDEX IF NOT EXISTS idx_property_address_id ON Property(address_id); |
| 54 | |
| 55 | -- За групирање по град |
| 56 | CREATE INDEX IF NOT EXISTS idx_address_city ON Address(city); |
| 57 | |
| 58 | -- Композитен индекс за плаќања со статус и датум |
| 59 | CREATE INDEX IF NOT EXISTS idx_payment_status_date ON Payment(status, payment_date); |
| 60 | }}} |
| 61 | |
| 62 | === 3. Детален месечен извештај за издавачите === #извештај3 |
| 63 | |
| 64 | Овој извештај е најсложен и користи повеќе CTE изрази со различни временски периоди. |
| 65 | |
| 66 | '''Клучни операции:''' |
| 67 | * Филтрирање по landlord_id |
| 68 | * Временски операции со start_date и end_date |
| 69 | * Проверка на статус на плаќања |
| 70 | |
| 71 | '''Потребни индекси:''' |
| 72 | |
| 73 | {{{#!sql |
| 74 | -- За брза идентификација на изадавачите |
| 75 | CREATE INDEX IF NOT EXISTS idx_lease_landlord_id ON Lease(landlord_id); |
| 76 | |
| 77 | -- За временски филтрирања на изнајмувања |
| 78 | CREATE INDEX IF NOT EXISTS idx_lease_start_date ON Lease(start_date); |
| 79 | CREATE INDEX IF NOT EXISTS idx_lease_end_date ON Lease(end_date); |
| 80 | |
| 81 | -- Композитен индекс за временски период на договор |
| 82 | CREATE INDEX IF NOT EXISTS idx_lease_dates ON Lease(start_date, end_date); |
| 83 | |
| 84 | -- За филтрирање активни договори |
| 85 | CREATE INDEX IF NOT EXISTS idx_lease_landlord_dates ON Lease(landlord_id, start_date, end_date); |
| 86 | |
| 87 | -- За сервисни барања |
| 88 | CREATE INDEX IF NOT EXISTS idx_service_request_status ON ServiceRequest(status); |
| 89 | CREATE INDEX IF NOT EXISTS idx_service_request_lease_id ON ServiceRequest(lease_id); |
| 90 | |
| 91 | -- За UserD табела (landlord профили) |
| 92 | CREATE INDEX IF NOT EXISTS idx_userd_id ON UserD(id); |
| 93 | }}} |
| 94 | |
| 95 | === 4. Извештај за платежна историја на станари === #извештај4 |
| 96 | |
| 97 | Фокусиран на tenant_id и различни статуси на плаќања. |
| 98 | |
| 99 | '''Потребни индекси:''' |
| 100 | |
| 101 | {{{#!sql |
| 102 | -- За идентификација на станари |
| 103 | CREATE INDEX IF NOT EXISTS idx_lease_tenant_id ON Lease(tenant_id); |
| 104 | CREATE INDEX IF NOT EXISTS idx_tenant_profile_id ON TenantProfile(id); |
| 105 | |
| 106 | -- За анализа на плаќања по статус |
| 107 | CREATE INDEX IF NOT EXISTS idx_payment_status ON Payment(status); |
| 108 | CREATE INDEX IF NOT EXISTS idx_payment_lease_status ON Payment(lease_id, status); |
| 109 | |
| 110 | -- За пресметка на просечна кирија |
| 111 | CREATE INDEX IF NOT EXISTS idx_lease_rent_amount ON Lease(rent_amount); |
| 112 | |
| 113 | -- За временска анализа на изнајмувања |
| 114 | CREATE INDEX IF NOT EXISTS idx_lease_tenant_dates ON Lease(tenant_id, start_date, end_date); |
| 115 | }}} |
| 116 | |
| 117 | === 5. Извештај за сервисни барања по општина === #извештај5 |
| 118 | |
| 119 | Анализира сервисни барања со географска и категориска сегментација. |
| 120 | |
| 121 | '''Потребни индекси:''' |
| 122 | |
| 123 | {{{#!sql |
| 124 | -- За категоризација на сервисни барања |
| 125 | CREATE INDEX IF NOT EXISTS idx_service_request_category ON ServiceRequest(service_category_id); |
| 126 | |
| 127 | -- За временска анализа на барања |
| 128 | CREATE INDEX IF NOT EXISTS idx_service_request_date ON ServiceRequest(request_date); |
| 129 | |
| 130 | -- За анализа на одржување |
| 131 | CREATE INDEX IF NOT EXISTS idx_maintenance_log_service_request ON MaintenanceLog(service_request_id); |
| 132 | CREATE INDEX IF NOT EXISTS idx_maintenance_log_date ON MaintenanceLog(maintenance_date); |
| 133 | |
| 134 | -- За географска анализа |
| 135 | CREATE INDEX IF NOT EXISTS idx_address_municipality ON Address(municipality); |
| 136 | CREATE INDEX IF NOT EXISTS idx_address_municipality_city ON Address(municipality, city); |
| 137 | |
| 138 | -- Композитен индекс за временска анализа на сервиси |
| 139 | CREATE INDEX IF NOT EXISTS idx_service_request_status_date ON ServiceRequest(status, request_date); |
| 140 | }}} |
| 141 | |
| 142 | === 6. Анализа на пазарни трендови === #извештај6 |
| 143 | |
| 144 | Анализира понуда и побарувачка по регион и тип на недвижност. |
| 145 | |
| 146 | '''Потребни индекси:''' |
| 147 | |
| 148 | {{{#!sql |
| 149 | -- За анализа по тип на недвижност |
| 150 | CREATE INDEX IF NOT EXISTS idx_property_type_id ON Property(property_type_id); |
| 151 | |
| 152 | -- За статус на огласи |
| 153 | CREATE INDEX IF NOT EXISTS idx_listing_status ON Listing(status); |
| 154 | |
| 155 | -- За пресметка на цена по квадратен метар |
| 156 | CREATE INDEX IF NOT EXISTS idx_unit_rent_area ON Unit(rent_amount, area_sq_m); |
| 157 | |
| 158 | -- За заинтересираност |
| 159 | CREATE INDEX IF NOT EXISTS idx_interested_listing_id ON Interested(listing_id); |
| 160 | CREATE INDEX IF NOT EXISTS idx_interested_tenant_profile ON Interested(tenant_profile_id); |
| 161 | |
| 162 | -- Композитен индекс за пазарна анализа |
| 163 | CREATE INDEX IF NOT EXISTS idx_listing_unit_status ON Listing(unit_id, status); |
| 164 | }}} |
| 165 | |
| 166 | == Тестирање на индексите со EXPLAIN ANALYZE == #тестирање |
| 167 | |
| 168 | За да се провери ефикасноста на индексите, можеме да користиме EXPLAIN ANALYZE: |
| 169 | |
| 170 | {{{#!sql |
| 171 | -- Пример за тестирање на Извештај 4 |
| 172 | EXPLAIN ANALYZE |
| 173 | WITH TenantPaymentStats AS ( |
| 174 | SELECT |
| 175 | t.id as tenant_id, |
| 176 | CONCAT(u.first_name, ' ', u.last_name) as tenant_name, |
| 177 | u.rating as tenant_rating, |
| 178 | COUNT(DISTINCT le.id) as total_leases, |
| 179 | COUNT(DISTINCT pay.id) as total_payments, |
| 180 | SUM(CASE WHEN pay.status = 'завршено' THEN pay.amount ELSE 0 END) as total_paid, |
| 181 | SUM(CASE WHEN pay.status = 'доцнење' THEN pay.amount ELSE 0 END) as late_payments_amount, |
| 182 | COUNT(CASE WHEN pay.status = 'доцнење' THEN 1 END) as late_payment_count, |
| 183 | COUNT(CASE WHEN pay.status = 'во тек' THEN 1 END) as pending_payment_count, |
| 184 | AVG(le.rent_amount) as avg_rent_amount, |
| 185 | MIN(le.start_date) as first_lease_date, |
| 186 | MAX(le.end_date) as latest_lease_end |
| 187 | FROM TenantProfile t |
| 188 | JOIN UserD u ON t.id = u.id |
| 189 | LEFT JOIN Lease le ON t.id = le.tenant_id |
| 190 | LEFT JOIN Payment pay ON le.id = pay.lease_id |
| 191 | GROUP BY t.id, u.first_name, u.last_name, u.rating |
| 192 | ) |
| 193 | SELECT |
| 194 | tenant_id, |
| 195 | tenant_name, |
| 196 | tenant_rating, |
| 197 | total_leases, |
| 198 | total_payments, |
| 199 | total_paid, |
| 200 | late_payments_amount, |
| 201 | late_payment_count, |
| 202 | pending_payment_count, |
| 203 | ROUND((late_payment_count * 100.0 / NULLIF(total_payments, 0)), 2) as late_payment_percentage, |
| 204 | avg_rent_amount, |
| 205 | first_lease_date, |
| 206 | latest_lease_end, |
| 207 | CASE |
| 208 | WHEN late_payment_count = 0 THEN 'Одличен' |
| 209 | WHEN late_payment_count <= 2 THEN 'Добар' |
| 210 | WHEN late_payment_count <= 5 THEN 'Среден' |
| 211 | ELSE 'Ризичен' |
| 212 | END as payment_reliability_score |
| 213 | FROM TenantPaymentStats |
| 214 | WHERE total_leases > 0 |
| 215 | ORDER BY late_payment_percentage ASC, total_paid DESC; |
| 216 | |
| 217 | }}} |
| 218 | |
| 219 | '''Што да бараме во резултатот:''' |
| 220 | * '''Index Scan''' наместо '''Seq Scan''' - значи дека индексот се користи |
| 221 | * '''Nested Loop''' со '''Index Scan''' на внатрешната страна на JOIN |
| 222 | * '''Execution time''' треба да биде значително намален |
| 223 | |
| 224 | |
| 225 | |
| 226 | |