| 10 | | - '''Стратегија на индексирање:''' Базата користи B-Tree индекси кои PostgreSQL автоматски ги креира за примарните клучеви(`PRIMARY KEY`) и уникатните ограничувања(`UNIQUE`). На тој начин е овозможен брз пристап до записите при пребарување по идентификатори на ентитетите, како и при проверка на единственоста на корисничките е-пошти. Индексите моментално постојат над колоните `user.user_id`, `user.email`, `transaction.transaction_id`, `transaction_account.transaction_account_id`, `transaction_breakdown.transaction_breakdown_id`, `tag.tag_id` и `tag_assigned_to_transaction.tag_assigned_to_transaction_id`. |
| 11 | | - '''Оптимизација преку релациски ограничувања:''' Сите врски помеѓу табелите се реализирани преку надворешни клучеви(`FOREIGN KEY`), со што PostgreSQL може ефикасно да го одржува интегритетот на податоците при вметнување, ажурирање и бришење на записи. |
| 12 | | - '''Подготвеност за понатамошна оптимизација:''' Со растот на количината на податоци, можно е дополнително индексирање на колони кои често се користат при филтрирање и поврзување на податоците, како што се `transaction.date`, `transaction_account.user_id`, `transaction_breakdown.transaction_id` и `tag_assigned_to_transaction.transaction_id`. Ваквата оптимизација е особено корисна кај статистичките извештаи и аналитичките прашалници кои обработуваат поголем број трансакции. |
| | 10 | Анализата е направена со `EXPLAIN (ANALYZE, BUFFERS)`, при што се споредува планот за извршување пред и по додавање на дополнителните индекси. Во почетната состојба базата ги содржи само индексите кои PostgreSQL автоматски ги креира за `PRIMARY KEY` и `UNIQUE` ограничувања. Овие индекси не се третираат како дополнителна оптимизација, туку како дел од основната релациска структура на базата. |
| | 11 | |
| | 12 | Во моментот на тестирањето, базата ја има следната количина на податоци: |
| | 13 | |
| | 14 | ||= Табела =||= Број на редови =|| |
| | 15 | || `user` || 3 || |
| | 16 | || `transaction` || 12 || |
| | 17 | || `transaction_account` || 5 || |
| | 18 | || `transaction_breakdown` || 11 || |
| | 19 | || `tag` || 11 || |
| | 20 | || `tag_assigned_to_transaction` || 16 || |
| | 21 | |
| | 22 | Поради малата количина на податоци, очекувано е PostgreSQL во повеќе случаи да избере `Seq Scan`, бидејќи целосното читање на мала табела е поевтино од пристап преку индекс. Затоа, во анализата не се гледа само времето на извршување, туку и дали индексот навистина се користи во execution plan-от. |
| | 23 | |
| | 24 | ==== Предложени дополнителни индекси ==== |
| | 25 | |
| | 26 | Следните индекси се предложени затоа што колоните често се користат во `JOIN` и `WHERE` услови во аналитичките извештаи: |
| | 27 | |
| | 28 | {{{#!sql |
| | 29 | CREATE INDEX IF NOT EXISTS idx_transaction_date |
| | 30 | ON "transaction"(date); |
| | 31 | |
| | 32 | CREATE INDEX IF NOT EXISTS idx_transaction_account_user_id |
| | 33 | ON transaction_account(user_id); |
| | 34 | |
| | 35 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_transaction_id |
| | 36 | ON transaction_breakdown(transaction_id); |
| | 37 | |
| | 38 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_account_id |
| | 39 | ON transaction_breakdown(transaction_account_id); |
| | 40 | |
| | 41 | CREATE INDEX IF NOT EXISTS idx_tag_assigned_transaction_id |
| | 42 | ON tag_assigned_to_transaction(transaction_id); |
| | 43 | |
| | 44 | CREATE INDEX IF NOT EXISTS idx_tag_assigned_tag_id |
| | 45 | ON tag_assigned_to_transaction(tag_id); |
| | 46 | }}} |
| | 47 | |
| | 48 | Индексот `idx_transaction_date` е наменет за извештаи кои филтрираат трансакции според временски период. Индексот `idx_transaction_account_user_id` е наменет за извештаи кои ги ограничуваат резултатите на конкретен корисник. Индексите на `transaction_breakdown` и `tag_assigned_to_transaction` се наменети за побрзо поврзување на трансакциите со сметки и тагови. |
| | 49 | |
| | 50 | ==== Сценарио 1: Трендови на трошење според тагови за корисник ==== |
| | 51 | |
| | 52 | '''Цел:''' |
| | 53 | Овој извештај ја прикажува потрошувачката по тагови за конкретен корисник во последните шест месеци. Прашалникот е комплексен затоа што ги поврзува табелите `tag_assigned_to_transaction`, `transaction`, `transaction_breakdown`, `transaction_account` и `tag`, а дополнително врши филтрирање по корисник и датум, групирање по таг и месец, и сумирање на потрошената сума. |
| | 54 | |
| | 55 | '''Анализиран SQL:''' |
| | 56 | |
| | 57 | {{{#!sql |
| | 58 | EXPLAIN (ANALYZE, BUFFERS) |
| | 59 | SELECT |
| | 60 | tg.tag_name, |
| | 61 | DATE_TRUNC('month', t.date) AS month, |
| | 62 | SUM(tb.spent_amount) AS total_spent |
| | 63 | FROM tag_assigned_to_transaction tat |
| | 64 | JOIN "transaction" t |
| | 65 | ON tat.transaction_id = t.transaction_id |
| | 66 | JOIN transaction_breakdown tb |
| | 67 | ON t.transaction_id = tb.transaction_id |
| | 68 | JOIN transaction_account ta |
| | 69 | ON tb.transaction_account_id = ta.transaction_account_id |
| | 70 | JOIN tag tg |
| | 71 | ON tat.tag_id = tg.tag_id |
| | 72 | WHERE |
| | 73 | ta.user_id = 1 |
| | 74 | AND t.date >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '6 MONTHS') |
| | 75 | GROUP BY |
| | 76 | tg.tag_name, month |
| | 77 | ORDER BY |
| | 78 | tg.tag_name, month; |
| | 79 | }}} |
| | 80 | |
| | 81 | '''Релевантни индекси за ова сценарио:''' |
| | 82 | |
| | 83 | {{{#!sql |
| | 84 | CREATE INDEX IF NOT EXISTS idx_transaction_date |
| | 85 | ON "transaction"(date); |
| | 86 | |
| | 87 | CREATE INDEX IF NOT EXISTS idx_transaction_account_user_id |
| | 88 | ON transaction_account(user_id); |
| | 89 | |
| | 90 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_transaction_id |
| | 91 | ON transaction_breakdown(transaction_id); |
| | 92 | |
| | 93 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_account_id |
| | 94 | ON transaction_breakdown(transaction_account_id); |
| | 95 | |
| | 96 | CREATE INDEX IF NOT EXISTS idx_tag_assigned_transaction_id |
| | 97 | ON tag_assigned_to_transaction(transaction_id); |
| | 98 | |
| | 99 | CREATE INDEX IF NOT EXISTS idx_tag_assigned_tag_id |
| | 100 | ON tag_assigned_to_transaction(tag_id); |
| | 101 | }}} |
| | 102 | |
| | 103 | '''План пред додавање на индексите:''' |
| | 104 | |
| | 105 | {{{#!sql |
| | 106 | GroupAggregate (cost=6.54..6.77 rows=9 width=53) (actual time=0.176..0.189 rows=7 loops=1) |
| | 107 | Group Key: tg.tag_name, (date_trunc('month'::text, t.date)) |
| | 108 | Buffers: shared hit=5 |
| | 109 | -> Sort (cost=6.54..6.57 rows=9 width=25) (actual time=0.162..0.167 rows=16 loops=1) |
| | 110 | Sort Key: tg.tag_name, (date_trunc('month'::text, t.date)) |
| | 111 | Sort Method: quicksort Memory: 25kB |
| | 112 | -> Hash Join (cost=4.99..6.40 rows=9 width=25) (actual time=0.119..0.142 rows=16 loops=1) |
| | 113 | Hash Cond: (tat.tag_id = tg.tag_id) |
| | 114 | -> Hash Join (cost=3.74..5.10 rows=9 width=16) (actual time=0.086..0.099 rows=16 loops=1) |
| | 115 | Hash Cond: (tat.transaction_id = t.transaction_id) |
| | 116 | -> Hash Join (cost=2.35..3.67 rows=10 width=16) (actual time=0.057..0.066 rows=16 loops=1) |
| | 117 | Hash Cond: (tat.transaction_id = tb.transaction_id) |
| | 118 | -> Seq Scan on tag_assigned_to_transaction tat |
| | 119 | -> Hash |
| | 120 | -> Hash Join |
| | 121 | Hash Cond: (tb.transaction_account_id = ta.transaction_account_id) |
| | 122 | -> Seq Scan on transaction_breakdown tb |
| | 123 | -> Hash |
| | 124 | -> Seq Scan on transaction_account ta |
| | 125 | Filter: (user_id = 1) |
| | 126 | Rows Removed by Filter: 2 |
| | 127 | -> Hash |
| | 128 | -> Seq Scan on transaction t |
| | 129 | Filter: (date >= date_trunc('month'::text, (CURRENT_DATE - '6 mons'::interval))) |
| | 130 | -> Hash |
| | 131 | -> Seq Scan on tag tg |
| | 132 | Planning Time: 0.733 ms |
| | 133 | Execution Time: 0.296 ms |
| | 134 | }}} |
| | 135 | |
| | 136 | '''План по додавање на индексите:''' |
| | 137 | |
| | 138 | {{{#!sql |
| | 139 | GroupAggregate (cost=6.54..6.77 rows=9 width=53) (actual time=0.154..0.166 rows=7 loops=1) |
| | 140 | Group Key: tg.tag_name, (date_trunc('month'::text, t.date)) |
| | 141 | Buffers: shared hit=5 |
| | 142 | -> Sort (cost=6.54..6.57 rows=9 width=25) (actual time=0.142..0.146 rows=16 loops=1) |
| | 143 | Sort Key: tg.tag_name, (date_trunc('month'::text, t.date)) |
| | 144 | Sort Method: quicksort Memory: 25kB |
| | 145 | -> Hash Join (cost=4.99..6.40 rows=9 width=25) (actual time=0.100..0.122 rows=16 loops=1) |
| | 146 | Hash Cond: (tat.tag_id = tg.tag_id) |
| | 147 | -> Hash Join (cost=3.74..5.10 rows=9 width=16) (actual time=0.068..0.081 rows=16 loops=1) |
| | 148 | Hash Cond: (tat.transaction_id = t.transaction_id) |
| | 149 | -> Hash Join (cost=2.35..3.67 rows=10 width=16) (actual time=0.040..0.049 rows=16 loops=1) |
| | 150 | Hash Cond: (tat.transaction_id = tb.transaction_id) |
| | 151 | -> Seq Scan on tag_assigned_to_transaction tat |
| | 152 | -> Hash |
| | 153 | -> Hash Join |
| | 154 | Hash Cond: (tb.transaction_account_id = ta.transaction_account_id) |
| | 155 | -> Seq Scan on transaction_breakdown tb |
| | 156 | -> Hash |
| | 157 | -> Seq Scan on transaction_account ta |
| | 158 | Filter: (user_id = 1) |
| | 159 | Rows Removed by Filter: 2 |
| | 160 | -> Hash |
| | 161 | -> Seq Scan on transaction t |
| | 162 | Filter: (date >= date_trunc('month'::text, (CURRENT_DATE - '6 mons'::interval))) |
| | 163 | -> Hash |
| | 164 | -> Seq Scan on tag tg |
| | 165 | Planning Time: 0.921 ms |
| | 166 | Execution Time: 0.242 ms |
| | 167 | }}} |
| | 168 | |
| | 169 | '''Споредба:''' |
| | 170 | |
| | 171 | ||= Метрика =||= Пред индекси =||= По индекси =|| |
| | 172 | || Planning Time || 0.733 ms || 0.921 ms || |
| | 173 | || Execution Time || 0.296 ms || 0.242 ms || |
| | 174 | || Тип на скенирање || `Seq Scan` || `Seq Scan` || |
| | 175 | || Дали новите индекси се користат || Не || Не || |
| | 176 | |
| | 177 | '''Заклучок:''' |
| | 178 | Времето на извршување се намали од 0.296 ms на 0.242 ms, што претставува намалување од приближно 18.2%. Сепак, execution plan-от и по додавање на индексите користи `Seq Scan` над сите релевантни табели, а не `Index Scan` или `Bitmap Index Scan`. Затоа подобрувањето не може да се припише директно на индексите. Најверојатната причина е малата количина на податоци и фактот што сите блокови се веќе во shared buffers. Индексите остануваат логични за идно зголемување на бројот на трансакции, тагови и breakdown записи. |
| | 179 | |
| | 180 | ==== Сценарио 2: Сметки со највисоко вкупно трошење во изминатата година ==== |
| | 181 | |
| | 182 | '''Цел:''' |
| | 183 | Овој извештај ги прикажува сметките со најголема вкупна потрошувачка во последната година. Прашалникот користи `JOIN` помеѓу `transaction_breakdown`, `transaction` и `transaction_account`, филтрирање по датум, `SUM` агрегација, `GROUP BY`, `ORDER BY` и `LIMIT`. |
| | 184 | |
| | 185 | '''Анализиран SQL:''' |
| | 186 | |
| | 187 | {{{#!sql |
| | 188 | EXPLAIN (ANALYZE, BUFFERS) |
| | 189 | SELECT |
| | 190 | ta.account_name, |
| | 191 | COALESCE(SUM(tb.spent_amount), 0) AS total_spent |
| | 192 | FROM transaction_breakdown tb |
| | 193 | JOIN "transaction" t |
| | 194 | ON tb.transaction_id = t.transaction_id |
| | 195 | JOIN transaction_account ta |
| | 196 | ON tb.transaction_account_id = ta.transaction_account_id |
| | 197 | WHERE |
| | 198 | t.date >= NOW() - INTERVAL '1 YEAR' |
| | 199 | GROUP BY |
| | 200 | ta.account_name |
| | 201 | ORDER BY |
| | 202 | total_spent DESC |
| | 203 | LIMIT 10; |
| | 204 | }}} |
| | 205 | |
| | 206 | '''Релевантни индекси за ова сценарио:''' |
| | 207 | |
| | 208 | {{{#!sql |
| | 209 | CREATE INDEX IF NOT EXISTS idx_transaction_date |
| | 210 | ON "transaction"(date); |
| | 211 | |
| | 212 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_transaction_id |
| | 213 | ON transaction_breakdown(transaction_id); |
| | 214 | |
| | 215 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_account_id |
| | 216 | ON transaction_breakdown(transaction_account_id); |
| | 217 | }}} |
| | 218 | |
| | 219 | '''План пред додавање на индексите:''' |
| | 220 | |
| | 221 | {{{#!sql |
| | 222 | Limit (cost=3.85..3.86 rows=5 width=40) (actual time=0.110..0.114 rows=3 loops=1) |
| | 223 | Buffers: shared hit=3 |
| | 224 | -> Sort (cost=3.85..3.86 rows=5 width=40) (actual time=0.108..0.111 rows=3 loops=1) |
| | 225 | Sort Key: (COALESCE(sum(tb.spent_amount), '0'::numeric)) DESC |
| | 226 | Sort Method: quicksort Memory: 25kB |
| | 227 | -> HashAggregate (cost=3.73..3.79 rows=5 width=40) (actual time=0.097..0.101 rows=3 loops=1) |
| | 228 | Group Key: ta.account_name |
| | 229 | -> Hash Join (cost=2.47..3.67 rows=11 width=12) (actual time=0.059..0.069 rows=11 loops=1) |
| | 230 | Hash Cond: (tb.transaction_account_id = ta.transaction_account_id) |
| | 231 | -> Hash Join (cost=1.36..2.51 rows=11 width=8) (actual time=0.032..0.038 rows=11 loops=1) |
| | 232 | Hash Cond: (tb.transaction_id = t.transaction_id) |
| | 233 | -> Seq Scan on transaction_breakdown tb |
| | 234 | -> Hash |
| | 235 | -> Seq Scan on transaction t |
| | 236 | Filter: (date >= (now() - '1 year'::interval)) |
| | 237 | -> Hash |
| | 238 | -> Seq Scan on transaction_account ta |
| | 239 | Planning Time: 0.403 ms |
| | 240 | Execution Time: 0.191 ms |
| | 241 | }}} |
| | 242 | |
| | 243 | '''План по додавање на индексите:''' |
| | 244 | |
| | 245 | {{{#!sql |
| | 246 | Limit (cost=3.85..3.86 rows=5 width=40) (actual time=0.100..0.104 rows=3 loops=1) |
| | 247 | Buffers: shared hit=3 |
| | 248 | -> Sort (cost=3.85..3.86 rows=5 width=40) (actual time=0.098..0.101 rows=3 loops=1) |
| | 249 | Sort Key: (COALESCE(sum(tb.spent_amount), '0'::numeric)) DESC |
| | 250 | Sort Method: quicksort Memory: 25kB |
| | 251 | -> HashAggregate (cost=3.73..3.79 rows=5 width=40) (actual time=0.086..0.090 rows=3 loops=1) |
| | 252 | Group Key: ta.account_name |
| | 253 | -> Hash Join (cost=2.47..3.67 rows=11 width=12) (actual time=0.060..0.070 rows=11 loops=1) |
| | 254 | Hash Cond: (tb.transaction_account_id = ta.transaction_account_id) |
| | 255 | -> Hash Join (cost=1.36..2.51 rows=11 width=8) (actual time=0.030..0.036 rows=11 loops=1) |
| | 256 | Hash Cond: (tb.transaction_id = t.transaction_id) |
| | 257 | -> Seq Scan on transaction_breakdown tb |
| | 258 | -> Hash |
| | 259 | -> Seq Scan on transaction t |
| | 260 | Filter: (date >= (now() - '1 year'::interval)) |
| | 261 | -> Hash |
| | 262 | -> Seq Scan on transaction_account ta |
| | 263 | Planning Time: 0.451 ms |
| | 264 | Execution Time: 0.166 ms |
| | 265 | }}} |
| | 266 | |
| | 267 | '''Споредба:''' |
| | 268 | |
| | 269 | ||= Метрика =||= Пред индекси =||= По индекси =|| |
| | 270 | || Planning Time || 0.403 ms || 0.451 ms || |
| | 271 | || Execution Time || 0.191 ms || 0.166 ms || |
| | 272 | || Тип на скенирање || `Seq Scan` || `Seq Scan` || |
| | 273 | || Дали новите индекси се користат || Не || Не || |
| | 274 | |
| | 275 | '''Заклучок:''' |
| | 276 | Времето на извршување се намали од 0.191 ms на 0.166 ms, што претставува намалување од приближно 13.1%. Сепак, планот за извршување не користи ниту еден од новите индекси. PostgreSQL продолжува со `Seq Scan` на `transaction`, `transaction_breakdown` и `transaction_account`, затоа што табелите имаат многу мал број редови и читањето на целата табела е поевтино од пристап преку индекс. Поради тоа, малото подобрување не се смета како директен ефект од индексите. Индексот на `transaction.date` сепак е оправдан за идно зголемување на бројот на трансакции, бидејќи извештајот филтрира според временски период. |
| | 277 | |
| | 278 | ==== Сценарио 3: Годишни трендови на трансакции за корисник ==== |
| | 279 | |
| | 280 | '''Цел:''' |
| | 281 | Овој извештај ја прикажува потрошувачката по квартали за конкретен корисник во тековната година. Прашалникот користи `JOIN` помеѓу `transaction_breakdown`, `transaction` и `transaction_account`, филтрирање по корисник и временски период, `SUM` агрегации со `CASE WHEN`, `GROUP BY` и `ORDER BY`. |
| | 282 | |
| | 283 | Во анализата условот за година е напишан како временски опсег, наместо само со `EXTRACT(YEAR FROM t.date)`, за B-Tree индексот на `date` да може поефикасно да се користи кога табелата ќе порасне. |
| | 284 | |
| | 285 | '''Анализиран SQL:''' |
| | 286 | |
| | 287 | {{{#!sql |
| | 288 | EXPLAIN (ANALYZE, BUFFERS) |
| | 289 | SELECT |
| | 290 | ta.account_name, |
| | 291 | SUM(CASE WHEN EXTRACT(QUARTER FROM t.date) = 1 THEN tb.spent_amount ELSE 0 END) AS q1_spent, |
| | 292 | SUM(CASE WHEN EXTRACT(QUARTER FROM t.date) = 2 THEN tb.spent_amount ELSE 0 END) AS q2_spent, |
| | 293 | SUM(CASE WHEN EXTRACT(QUARTER FROM t.date) = 3 THEN tb.spent_amount ELSE 0 END) AS q3_spent, |
| | 294 | SUM(CASE WHEN EXTRACT(QUARTER FROM t.date) = 4 THEN tb.spent_amount ELSE 0 END) AS q4_spent |
| | 295 | FROM transaction_breakdown tb |
| | 296 | JOIN "transaction" t |
| | 297 | ON tb.transaction_id = t.transaction_id |
| | 298 | JOIN transaction_account ta |
| | 299 | ON tb.transaction_account_id = ta.transaction_account_id |
| | 300 | WHERE |
| | 301 | ta.user_id = 1 |
| | 302 | AND t.date >= DATE_TRUNC('year', CURRENT_DATE) |
| | 303 | AND t.date < DATE_TRUNC('year', CURRENT_DATE) + INTERVAL '1 YEAR' |
| | 304 | GROUP BY |
| | 305 | ta.account_name |
| | 306 | ORDER BY |
| | 307 | ta.account_name; |
| | 308 | }}} |
| | 309 | |
| | 310 | '''Релевантни индекси за ова сценарио:''' |
| | 311 | |
| | 312 | {{{#!sql |
| | 313 | CREATE INDEX IF NOT EXISTS idx_transaction_date |
| | 314 | ON "transaction"(date); |
| | 315 | |
| | 316 | CREATE INDEX IF NOT EXISTS idx_transaction_account_user_id |
| | 317 | ON transaction_account(user_id); |
| | 318 | |
| | 319 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_transaction_id |
| | 320 | ON transaction_breakdown(transaction_id); |
| | 321 | |
| | 322 | CREATE INDEX IF NOT EXISTS idx_transaction_breakdown_account_id |
| | 323 | ON transaction_breakdown(transaction_account_id); |
| | 324 | }}} |
| | 325 | |
| | 326 | '''План пред додавање на индексите:''' |
| | 327 | |
| | 328 | {{{#!sql |
| | 329 | Sort (cost=4.16..4.17 rows=3 width=136) (actual time=0.141..0.144 rows=3 loops=1) |
| | 330 | Sort Key: ta.account_name |
| | 331 | Sort Method: quicksort Memory: 25kB |
| | 332 | Buffers: shared hit=3 |
| | 333 | -> HashAggregate (cost=4.08..4.14 rows=3 width=136) (actual time=0.126..0.131 rows=3 loops=1) |
| | 334 | Group Key: ta.account_name |
| | 335 | -> Hash Join (cost=2.35..3.85 rows=7 width=20) (actual time=0.070..0.089 rows=11 loops=1) |
| | 336 | Hash Cond: (t.transaction_id = tb.transaction_id) |
| | 337 | -> Seq Scan on transaction t |
| | 338 | Filter: ((date >= date_trunc('year'::text, (CURRENT_DATE)::timestamp with time zone)) |
| | 339 | AND (date < (date_trunc('year'::text, (CURRENT_DATE)::timestamp with time zone) + '1 year'::interval))) |
| | 340 | -> Hash |
| | 341 | -> Hash Join |
| | 342 | Hash Cond: (tb.transaction_account_id = ta.transaction_account_id) |
| | 343 | -> Seq Scan on transaction_breakdown tb |
| | 344 | -> Hash |
| | 345 | -> Seq Scan on transaction_account ta |
| | 346 | Filter: (user_id = 1) |
| | 347 | Rows Removed by Filter: 2 |
| | 348 | Planning Time: 0.402 ms |
| | 349 | Execution Time: 0.242 ms |
| | 350 | }}} |
| | 351 | |
| | 352 | '''План по додавање на индексите:''' |
| | 353 | |
| | 354 | {{{#!sql |
| | 355 | Sort (cost=4.16..4.17 rows=3 width=136) (actual time=0.131..0.135 rows=3 loops=1) |
| | 356 | Sort Key: ta.account_name |
| | 357 | Sort Method: quicksort Memory: 25kB |
| | 358 | Buffers: shared hit=3 |
| | 359 | -> HashAggregate (cost=4.08..4.14 rows=3 width=136) (actual time=0.117..0.122 rows=3 loops=1) |
| | 360 | Group Key: ta.account_name |
| | 361 | -> Hash Join (cost=2.35..3.85 rows=7 width=20) (actual time=0.063..0.082 rows=11 loops=1) |
| | 362 | Hash Cond: (t.transaction_id = tb.transaction_id) |
| | 363 | -> Seq Scan on transaction t |
| | 364 | Filter: ((date >= date_trunc('year'::text, (CURRENT_DATE)::timestamp with time zone)) |
| | 365 | AND (date < (date_trunc('year'::text, (CURRENT_DATE)::timestamp with time zone) + '1 year'::interval))) |
| | 366 | -> Hash |
| | 367 | -> Hash Join |
| | 368 | Hash Cond: (tb.transaction_account_id = ta.transaction_account_id) |
| | 369 | -> Seq Scan on transaction_breakdown tb |
| | 370 | -> Hash |
| | 371 | -> Seq Scan on transaction_account ta |
| | 372 | Filter: (user_id = 1) |
| | 373 | Rows Removed by Filter: 2 |
| | 374 | Planning Time: 0.500 ms |
| | 375 | Execution Time: 0.212 ms |
| | 376 | }}} |
| | 377 | |
| | 378 | '''Споредба:''' |
| | 379 | |
| | 380 | ||= Метрика =||= Пред индекси =||= По индекси =|| |
| | 381 | || Planning Time || 0.402 ms || 0.500 ms || |
| | 382 | || Execution Time || 0.242 ms || 0.212 ms || |
| | 383 | || Тип на скенирање || `Seq Scan` || `Seq Scan` || |
| | 384 | || Дали новите индекси се користат || Не || Не || |
| | 385 | |
| | 386 | '''Заклучок:''' |
| | 387 | Времето на извршување се намали од 0.242 ms на 0.212 ms, што претставува намалување од приближно 12.4%. Сепак, планот останува ист во однос на пристапот до табелите: PostgreSQL користи `Seq Scan`, а не новите индекси. Причината е малата количина на податоци: `transaction` има 12 редови, `transaction_breakdown` има 11 редови, а `transaction_account` има 5 редови. За ваква големина, секвенцијалното читање е поевтино од пристап преку индекс. Индексите сепак се соодветни за идно зголемување на базата, особено затоа што извештајот филтрира по `user_id` и по временски период. |
| | 388 | |
| | 389 | ==== Финален заклучок од анализата ==== |
| | 390 | |
| | 391 | Во сите три сценарија, PostgreSQL продолжи да користи `Seq Scan` и по додавање на предложените индекси. Тоа значи дека индексите не беа реално искористени во моменталниот execution plan. |
| | 392 | |
| | 393 | Ова однесување е очекувано поради малата количина на податоци во тест базата. Во моментот на тестирањето, најголемите релевантни табели имаат само 12 трансакции, 11 breakdown записи и 16 tag-assignment записи. За вакви табели, PostgreSQL правилно проценува дека целосно скенирање на табелата е поевтино од користење индекс. |
| | 394 | |
| | 395 | Иако execution time се намали во сите три сценарија, тоа не може директно да се припише на индексите, бидејќи execution plan-от не покажува `Index Scan`, `Bitmap Index Scan` или `Index Only Scan`. Малите разлики во времето најверојатно се резултат на кеширање, повторно користење на податоци во shared buffers и нормална варијација при извршување. |
| | 396 | |
| | 397 | Сепак, предложените индекси остануваат оправдани како подготовка за поголема количина на податоци. Со раст на бројот на корисници, трансакции, сметки, тагови и breakdown записи, индексите на `transaction.date`, `transaction_account.user_id`, `transaction_breakdown.transaction_id`, `transaction_breakdown.transaction_account_id`, `tag_assigned_to_transaction.transaction_id` и `tag_assigned_to_transaction.tag_id` ќе овозможат поефикасно филтрирање и поврзување на податоците во аналитичките извештаи. |
| | 398 | |