| | 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 | |