| 146 | | '''Пред додавање на индексите:''' |
| 147 | | |
| 148 | | {{{ |
| 149 | | HashAggregate (cost=45.20..47.30 rows=180 width=68) (actual time=0.350..0.360 rows=50 loops=1) |
| 150 | | Group Key: p.product_id, p.name, c.name |
| 151 | | Buffers: shared hit=25 |
| 152 | | -> Hash Join (cost=8.50..42.00 rows=200 width=44) (actual time=0.150..0.280 rows=50 loops=1) |
| 153 | | Hash Cond: (oi.product_id = p.product_id) |
| 154 | | -> Hash Join (cost=5.00..30.00 rows=50 width=20) (actual time=0.080..0.180 rows=50 loops=1) |
| 155 | | Hash Cond: (oi.order_id = o.order_id) |
| 156 | | -> Seq Scan on order_item oi (actual time=0.010..0.020 rows=50 loops=1) |
| 157 | | -> Hash (actual time=0.030..0.030 rows=46 loops=1) |
| 158 | | -> Seq Scan on orders o (actual time=0.010..0.020 rows=46 loops=1) |
| 159 | | Filter: (status = 'ПЛАТЕНА' AND created_at >= ...) |
| 160 | | -> Hash |
| 161 | | -> Seq Scan on product p |
| 162 | | -> Hash |
| 163 | | -> Seq Scan on category c |
| 164 | | Planning Time: 0.350 ms |
| 165 | | Execution Time: 0.480 ms |
| 166 | | }}} |
| 167 | | |
| 168 | | '''По додавање на индексите:''' |
| 169 | | |
| 170 | | {{{ |
| 171 | | HashAggregate (cost=40.20..42.30 rows=180 width=68) (actual time=0.250..0.260 rows=50 loops=1) |
| 172 | | Group Key: p.product_id, p.name, c.name |
| 173 | | Buffers: shared hit=30 |
| 174 | | -> Hash Join (cost=7.50..38.00 rows=200 width=44) (actual time=0.120..0.220 rows=50 loops=1) |
| 175 | | Hash Cond: (oi.product_id = p.product_id) |
| 176 | | -> Hash Join (cost=4.50..28.00 rows=50 width=20) (actual time=0.070..0.150 rows=50 loops=1) |
| 177 | | Hash Cond: (oi.order_id = o.order_id) |
| 178 | | -> Seq Scan on order_item oi (actual time=0.010..0.020 rows=50 loops=1) |
| 179 | | -> Hash (actual time=0.025..0.025 rows=46 loops=1) |
| 180 | | -> Index Scan using idx_orders_status on orders o |
| 181 | | Index Cond: (status = 'ПЛАТЕНА') |
| 182 | | Filter: (created_at >= ...) |
| 183 | | -> Hash |
| 184 | | -> Seq Scan on product p |
| 185 | | -> Hash |
| 186 | | -> Seq Scan on category c |
| 187 | | Planning Time: 0.400 ms |
| 188 | | Execution Time: 0.450 ms |
| | 146 | Резултат од EXPLAIN (ANALYZE, BUFFERS): |
| | 147 | |
| | 148 | {{{ |
| | 149 | Sort (cost=916.67..916.77 rows=40 width=329) (actual time=110.670..110.675 rows=24 loops=1) |
| | 150 | Sort Key: (sum(((oi.quantity)::numeric * oi.unit_price))) DESC |
| | 151 | Sort Method: quicksort Memory: 27kB |
| | 152 | Buffers: shared hit=42572 |
| | 153 | -> GroupAggregate (cost=914.00..915.60 rows=40 width=329) (actual time=98.576..110.651 rows=24 loops=1) |
| | 154 | Group Key: p.product_id, c.name |
| | 155 | Buffers: shared hit=42572 |
| | 156 | -> Sort (cost=914.00..914.10 rows=40 width=273) (actual time=98.557..99.821 rows=20940 loops=1) |
| | 157 | Sort Key: p.product_id, c.name, o.order_id |
| | 158 | Sort Method: quicksort Memory: 2078kB |
| | 159 | Buffers: shared hit=42572 |
| | 160 | -> Nested Loop (cost=97.89..912.94 rows=40 width=273) (actual time=5.056..56.400 rows=20940 loops=1) |
| | 161 | Buffers: shared hit=42572 |
| | 162 | -> Nested Loop (cost=97.74..908.87 rows=40 width=59) (actual time=5.043..46.378 rows=20940 loops=1) |
| | 163 | Buffers: shared hit=42562 |
| | 164 | -> Hash Join (cost=97.60..902.23 rows=40 width=28) (actual time=5.030..17.485 rows=20940 loops=1) |
| | 165 | Hash Cond: (oi.order_id = o.order_id) |
| | 166 | Buffers: shared hit=682 |
| | 167 | -> Seq Scan on order_item oi (cost=0.00..741.48 rows=24048 width=28) (actual time=0.019..4.361 rows=30048 loops=1) |
| | 168 | Buffers: shared hit=501 |
| | 169 | -> Hash (cost=97.43..97.43 rows=13 width=4) (actual time=4.999..5.000 rows=7010 loops=1) |
| | 170 | Buckets: 8192 (originally 1024) Batches: 1 (originally 1) Memory Usage: 311kB |
| | 171 | Buffers: shared hit=181 |
| | 172 | -> Bitmap Heap Scan on orders o (cost=4.58..97.43 rows=13 width=4) (actual time=0.485..3.863 rows=7010 loops=1) |
| | 173 | Recheck Cond: ((status)::text = 'ПЛАТЕНА'::text) |
| | 174 | Filter: (created_at >= (now() - '1 year'::interval)) |
| | 175 | Heap Blocks: exact=168 |
| | 176 | Buffers: shared hit=181 |
| | 177 | -> Bitmap Index Scan on idx_orders_status (cost=0.00..4.58 rows=39 width=0) (actual time=0.443..0.443 rows=13992 loops=1) |
| | 178 | Index Cond: ((status)::text = 'ПЛАТЕНА'::text) |
| | 179 | Buffers: shared hit=13 |
| | 180 | -> Index Scan using product_pkey on product p (cost=0.15..0.17 rows=1 width=35) (actual time=0.001..0.001 rows=1 loops=20940) |
| | 181 | Index Cond: (product_id = oi.product_id) |
| | 182 | Buffers: shared hit=41880 |
| | 183 | -> Memoize (cost=0.15..0.20 rows=1 width=222) (actual time=0.000..0.000 rows=1 loops=20940) |
| | 184 | Cache Key: p.category_id |
| | 185 | Cache Mode: logical |
| | 186 | Hits: 20935 Misses: 5 Evictions: 0 Overflows: 0 Memory Usage: 1kB |
| | 187 | Buffers: shared hit=10 |
| | 188 | -> Index Scan using category_pkey on category c (cost=0.14..0.19 rows=1 width=222) (actual time=0.002..0.002 rows=1 loops=5) |
| | 189 | Index Cond: (category_id = p.category_id) |
| | 190 | Buffers: shared hit=10 |
| | 191 | Planning: |
| | 192 | Buffers: shared hit=11 |
| | 193 | Planning Time: 0.559 ms |
| | 194 | Execution Time: 110.770 ms |
| 193 | | || Метрика || Пред индекси || По индекси || |
| 194 | | || Planning Time || 0.350 ms || 0.400 ms || |
| 195 | | || Execution Time || 0.480 ms || 0.450 ms || |
| 196 | | || Подобрување || / || ~6% || |
| 197 | | || Тип на скенирање || Seq Scan на orders || Index Scan со idx_orders_status || |
| 198 | | || Дали индексите се користат || Не || Да || |
| 199 | | |
| 200 | | '''Заклучок:''' По додавање на индексите, PostgreSQL го користи `idx_orders_status` за побрзо да ги најде платените нарачки. Подобрувањето е мало (6%) поради малиот број на податоци, но со растот на базата разликата значително ќе се зголеми. |
| | 199 | || Метрика || Вредност || |
| | 200 | || Planning Time || 0.559 ms || |
| | 201 | || Execution Time || 110.770 ms || |
| | 202 | || Вкупно вратени редови || 24 || |
| | 203 | || Тип на скенирање на orders || '''Bitmap Index Scan (idx_orders_status)''' || |
| | 204 | || Индекси искористени || idx_orders_status, product_pkey, category_pkey || |
| | 205 | || Memoize оптимизација || Да (20.935 hits, 5 misses) || |
| | 206 | |
| | 207 | '''Заклучок:''' Со растот на базата од 46 на 10.000+ нарачки, PostgreSQL почна да го користи индексот `idx_orders_status` преку `Bitmap Index Scan` за филтрирање на платените нарачки, наместо Seq Scan. Дополнително, PostgreSQL користи `Memoize` оптимизација за категориите, намалувајќи 20.935 повторени читања на само 5. |
| 224 | | '''Пред додавање на индексите:''' |
| 225 | | |
| 226 | | {{{ |
| 227 | | GroupAggregate (cost=35.20..37.30 rows=12 width=68) (actual time=0.280..0.290 rows=12 loops=1) |
| 228 | | Group Key: (to_char(payment_date, 'YYYY-MM')) |
| 229 | | Buffers: shared hit=18 |
| 230 | | -> Sort (cost=35.20..35.30 rows=50 width=36) (actual time=0.270..0.275 rows=50 loops=1) |
| 231 | | Sort Key: (to_char(p.payment_date, 'YYYY-MM')) |
| 232 | | -> Hash Join (cost=15.00..33.00 rows=50 width=36) (actual time=0.100..0.250 rows=50 loops=1) |
| 233 | | Hash Cond: (p.order_id = o.order_id) |
| 234 | | -> Seq Scan on payment p (actual time=0.010..0.020 rows=50 loops=1) |
| 235 | | -> Hash (actual time=0.030..0.030 rows=46 loops=1) |
| 236 | | -> Seq Scan on orders o (actual time=0.010..0.020 rows=46 loops=1) |
| 237 | | Filter: (status = 'ПЛАТЕНА') |
| 238 | | Planning Time: 0.380 ms |
| 239 | | Execution Time: 0.520 ms |
| 240 | | }}} |
| 241 | | |
| 242 | | '''По додавање на индексите:''' |
| 243 | | |
| 244 | | {{{ |
| 245 | | GroupAggregate (cost=28.20..30.30 rows=12 width=68) (actual time=0.180..0.190 rows=12 loops=1) |
| 246 | | Group Key: (to_char(payment_date, 'YYYY-MM')) |
| 247 | | Buffers: shared hit=20 |
| 248 | | -> Sort (cost=28.20..28.30 rows=50 width=36) (actual time=0.170..0.175 rows=50 loops=1) |
| 249 | | Sort Key: (to_char(p.payment_date, 'YYYY-MM')) |
| 250 | | -> Hash Join (cost=10.00..26.00 rows=50 width=36) (actual time=0.070..0.150 rows=50 loops=1) |
| 251 | | Hash Cond: (p.order_id = o.order_id) |
| 252 | | -> Seq Scan on payment p (actual time=0.010..0.015 rows=50 loops=1) |
| 253 | | -> Hash (actual time=0.020..0.020 rows=46 loops=1) |
| 254 | | -> Index Scan using idx_orders_status on orders o |
| 255 | | Index Cond: (status = 'ПЛАТЕНА') |
| 256 | | Planning Time: 0.420 ms |
| 257 | | Execution Time: 0.490 ms |
| | 231 | Резултат од EXPLAIN (ANALYZE, BUFFERS): |
| | 232 | |
| | 233 | {{{ |
| | 234 | GroupAggregate (cost=681.74..804.98 rows=3521 width=120) (actual time=15.977..18.246 rows=13 loops=1) |
| | 235 | Group Key: (to_char(p.payment_date, 'YYYY-MM'::text)) |
| | 236 | Buffers: shared hit=208 |
| | 237 | -> Sort (cost=681.74..690.55 rows=3521 width=50) (actual time=15.791..16.167 rows=5046 loops=1) |
| | 238 | Sort Key: (to_char(p.payment_date, 'YYYY-MM'::text)) DESC, o.order_id |
| | 239 | Sort Method: quicksort Memory: 429kB |
| | 240 | Buffers: shared hit=208 |
| | 241 | -> Hash Join (cost=153.54..474.32 rows=3521 width=50) (actual time=1.766..8.294 rows=5046 loops=1) |
| | 242 | Hash Cond: (o.order_id = p.order_id) |
| | 243 | Buffers: shared hit=208 |
| | 244 | -> Seq Scan on orders o (cost=0.00..293.57 rows=7010 width=12) (actual time=0.018..2.539 rows=7010 loops=1) |
| | 245 | Filter: ((status)::text = 'ПЛАТЕНА'::text) |
| | 246 | Rows Removed by Filter: 3036 |
| | 247 | Buffers: shared hit=168 |
| | 248 | -> Hash (cost=90.46..90.46 rows=5046 width=18) (actual time=1.719..1.720 rows=5046 loops=1) |
| | 249 | Buckets: 8192 Batches: 1 Memory Usage: 321kB |
| | 250 | Buffers: shared hit=40 |
| | 251 | -> Seq Scan on payment p (cost=0.00..90.46 rows=5046 width=18) (actual time=0.007..0.750 rows=5046 loops=1) |
| | 252 | Buffers: shared hit=40 |
| | 253 | Planning: |
| | 254 | Buffers: shared hit=93 |
| | 255 | Planning Time: 0.793 ms |
| | 256 | Execution Time: 18.309 ms |
| 262 | | || Метрика || Пред индекси || По индекси || |
| 263 | | || Planning Time || 0.380 ms || 0.420 ms || |
| 264 | | || Execution Time || 0.520 ms || 0.490 ms || |
| 265 | | || Подобрување || / || ~6% || |
| 266 | | || Тип на скенирање || Seq Scan на orders || Index Scan со idx_orders_status || |
| 267 | | || Дали индексите се користат || Не || Да || |
| | 261 | || Метрика || Вредност || |
| | 262 | || Planning Time || 0.793 ms || |
| | 263 | || Execution Time || 18.309 ms || |
| | 264 | || Вкупно вратени редови || 13 (месеци) || |
| | 265 | || Тип на скенирање на orders || Seq Scan (со Filter на status) || |
| | 266 | || Индекси искористени || (не се користат поради мал број на вратени редови) || |
| | 267 | |
| | 268 | '''Заклучок:''' Овој извештај враќа само 13 редови (месеци) и PostgreSQL избира Seq Scan на двете табели, бидејќи агрегацијата бара читање на сите 5.046 плаќања. Индексот `idx_payment_payment_date` не се користи бидејќи нема филтер по датум (сите плаќања се во опсегот). |
| 289 | | '''Пред додавање на индексите:''' |
| 290 | | |
| 291 | | {{{ |
| 292 | | Sort (cost=42.00..42.50 rows=200 width=52) (actual time=0.350..0.355 rows=12 loops=1) |
| 293 | | Sort Key: (sum((oi.quantity * oi.unit_price))) DESC |
| 294 | | Buffers: shared hit=22 |
| 295 | | -> HashAggregate (cost=35.00..37.00 rows=200 width=52) (actual time=0.320..0.330 rows=12 loops=1) |
| 296 | | Group Key: (date_part('hour', o.created_at)) |
| 297 | | -> Hash Join (cost=15.00..32.00 rows=300 width=36) (actual time=0.100..0.250 rows=50 loops=1) |
| 298 | | Hash Cond: (oi.order_id = o.order_id) |
| 299 | | -> Seq Scan on order_item oi (actual time=0.010..0.020 rows=50 loops=1) |
| 300 | | -> Hash (actual time=0.030..0.030 rows=46 loops=1) |
| 301 | | -> Seq Scan on orders o (actual time=0.010..0.020 rows=46 loops=1) |
| 302 | | Filter: (status = 'ПЛАТЕНА') |
| 303 | | Planning Time: 0.400 ms |
| 304 | | Execution Time: 0.550 ms |
| 305 | | }}} |
| 306 | | |
| 307 | | '''По додавање на индексите:''' |
| 308 | | |
| 309 | | {{{ |
| 310 | | Sort (cost=35.00..35.50 rows=200 width=52) (actual time=0.250..0.255 rows=12 loops=1) |
| 311 | | Sort Key: (sum((oi.quantity * oi.unit_price))) DESC |
| 312 | | Buffers: shared hit=25 |
| 313 | | -> HashAggregate (cost=28.00..30.00 rows=200 width=52) (actual time=0.220..0.230 rows=12 loops=1) |
| 314 | | Group Key: (date_part('hour', o.created_at)) |
| 315 | | -> Hash Join (cost=10.00..25.00 rows=300 width=36) (actual time=0.070..0.150 rows=50 loops=1) |
| 316 | | Hash Cond: (oi.order_id = o.order_id) |
| 317 | | -> Seq Scan on order_item oi (actual time=0.010..0.020 rows=50 loops=1) |
| 318 | | -> Hash (actual time=0.020..0.020 rows=46 loops=1) |
| 319 | | -> Index Scan using idx_orders_status on orders o |
| 320 | | Index Cond: (status = 'ПЛАТЕНА') |
| 321 | | Planning Time: 0.420 ms |
| 322 | | Execution Time: 0.500 ms |
| | 290 | Резултат од EXPLAIN (ANALYZE, BUFFERS): |
| | 291 | |
| | 292 | {{{ |
| | 293 | Sort (cost=3738.84..3756.37 rows=7010 width=80) (actual time=61.237..61.241 rows=24 loops=1) |
| | 294 | Sort Key: (sum(((oi.quantity)::numeric * oi.unit_price))) DESC |
| | 295 | Sort Method: quicksort Memory: 26kB |
| | 296 | Buffers: shared hit=669 |
| | 297 | -> GroupAggregate (cost=2819.00..3291.07 rows=7010 width=80) (actual time=50.305..61.219 rows=24 loops=1) |
| | 298 | Group Key: (EXTRACT(hour FROM o.created_at)) |
| | 299 | Buffers: shared hit=669 |
| | 300 | -> Sort (cost=2819.00..2871.42 rows=20967 width=45) (actual time=49.774..51.274 rows=20940 loops=1) |
| | 301 | Sort Key: (EXTRACT(hour FROM o.created_at)), o.order_id |
| | 302 | Sort Method: quicksort Memory: 1586kB |
| | 303 | Buffers: shared hit=669 |
| | 304 | -> Hash Join (cost=381.20..1314.01 rows=20967 width=45) (actual time=3.765..20.296 rows=20940 loops=1) |
| | 305 | Hash Cond: (oi.order_id = o.order_id) |
| | 306 | Buffers: shared hit=669 |
| | 307 | -> Seq Scan on order_item oi (cost=0.00..801.48 rows=30048 width=13) (actual time=0.014..2.618 rows=30048 loops=1) |
| | 308 | Buffers: shared hit=501 |
| | 309 | -> Hash (cost=293.57..293.57 rows=7010 width=12) (actual time=3.724..3.725 rows=7010 loops=1) |
| | 310 | Buckets: 8192 Batches: 1 Memory Usage: 366kB |
| | 311 | Buffers: shared hit=168 |
| | 312 | -> Seq Scan on orders o (cost=0.00..293.57 rows=7010 width=12) (actual time=0.011..2.333 rows=7010 loops=1) |
| | 313 | Filter: ((status)::text = 'ПЛАТЕНА'::text) |
| | 314 | Rows Removed by Filter: 3036 |
| | 315 | Buffers: shared hit=168 |
| | 316 | Planning: |
| | 317 | Buffers: shared hit=46 |
| | 318 | Planning Time: 0.582 ms |
| | 319 | Execution Time: 61.307 ms |
| 327 | | || Метрика || Пред индекси || По индекси || |
| 328 | | || Planning Time || 0.400 ms || 0.420 ms || |
| 329 | | || Execution Time || 0.550 ms || 0.500 ms || |
| 330 | | || Подобрување || / || ~9% || |
| 331 | | || Тип на скенирање || Seq Scan на orders || Index Scan со idx_orders_status || |
| 332 | | || Дали индексите се користат || Не || Да || |
| | 324 | || Метрика || Вредност || |
| | 325 | || Planning Time || 0.582 ms || |
| | 326 | || Execution Time || 61.307 ms || |
| | 327 | || Вкупно вратени редови || 24 (часови) || |
| | 328 | || Тип на скенирање на orders || Seq Scan (со Filter на status) || |
| | 329 | || Тип на скенирање на order_item || Seq Scan || |
| | 330 | || Индекси искористени || (не се користат поради голем опсег на податоци) || |
| | 331 | |
| | 332 | '''Заклучок:''' Овој извештај враќа само 24 редови (часови), но мора да ги прочита сите 30.048 ставки и 7.010 платени нарачки. PostgreSQL избира Seq Scan бидејќи индексот не помага при агрегација на сите податоци. |
| 336 | | || Сценарио || Execution Time пред индекси || Execution Time по индекси || Подобрување || Индекси искористени || |
| 337 | | || Најпрофитабилни производи || 0.480 ms || 0.450 ms || ~6% || Да || |
| 338 | | || Месечни приходи || 0.520 ms || 0.490 ms || ~6% || Да || |
| 339 | | || Најпрометни часови || 0.550 ms || 0.500 ms || ~9% || Да || |
| 340 | | |
| 341 | | Со оглед на малиот број на податоци во тест-базата, подобрувањето е мало. Меѓутоа, со растот на базата (илјадници нарачки, десетици илјади ставки), овие индекси значително ќе го намалат времето на извршување на аналитичките извештаи. |
| 342 | | |
| 343 | | Најзначаен е индексот `idx_orders_status`, кој се користи во сите три сценарија за филтрирање на платените нарачки. |
| | 336 | || Сценарио || Execution Time || Вратени редови || Индекси искористени || |
| | 337 | || Најпрофитабилни производи || 110.770 ms || 24 || idx_orders_status || |
| | 338 | || Месечни приходи || 18.309 ms || 13 || (Seq Scan) || |
| | 339 | || Најпрометни часови || 61.307 ms || 24 || (Seq Scan) || |
| | 340 | |
| | 341 | '''Клучни наоди:''' |
| | 342 | |
| | 343 | * '''`idx_orders_status` се користи''' во Сценарио 1 преку `Bitmap Index Scan`, што покажува дека индексот е ефективен кога има филтер по статус. |
| | 344 | * '''Memoize оптимизација''' се користи за категориите во Сценарио 1, намалувајќи 20.935 повторени читања на само 5. |
| | 345 | * Во Сценарија 2 и 3, PostgreSQL избира Seq Scan бидејќи агрегацијата бара читање на сите податоци (нема селективен филтер). |
| | 346 | * Со растот на базата, индексите ќе имаат сè поголемо влијание, особено `idx_orders_status` и `idx_orders_created_at`. |