Changes between Initial Version and Version 1 of QueryOptimization


Ignore:
Timestamp:
05/19/26 17:40:47 (7 days ago)
Author:
231138
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • QueryOptimization

    v1 v1  
     1= Напредни бази на податоци =
     2= Фаза 4 — Индекси и оптимизација на прашалници =
     3== Проект: DriveNet ==
     4
     5Александар Милошевски 231138
     6Исидора Кузмановска 231052
     7Андон Михајлов 231016
     8
     9Во оваа фаза ги анализираме погледите дефинирани во Фаза 2 преку прашалници базирани на реални сценарија кои ќе бидат присутни во нашата апликација и истите ги оптимизираме со помош на индекси.
     10
     11=== 1. Анализа на поглед 1, достапни возења по град на поаѓање ===
     12
     13Прашалникот кој го тестираме:
     14{{{
     15SELECT * FROM v_available_rides WHERE origin_city = 'Скопје' LIMIT 50;
     16}}}
     17
     18==== Време на извршување без индекси:
     19
     20**2 s 92 ms**
     21
     22{{{
     23Limit  (cost=473.63..24708.84 rows=50 width=223)
     24       (actual time=2040.xxx..2092.xxx rows=0 loops=1)
     25  ->  Hash Join
     26        Hash Cond: (ud.id = vdp.driver_user_id)
     27        ->  Seq Scan on rides r
     28              Filter: ((status)::text = 'scheduled'::text)
     29              Rows Removed by Filter: 4750535
     30        ->  Hash
     31              ->  Finalize HashAggregate
     32                    Group Key: ratings.reviewee_user_id
     33                    ->  Gather
     34                          Workers Planned: 3
     35                          ->  Partial HashAggregate
     36                                ->  Parallel Seq Scan on ratings
     37                                      (603662 rows — full scan за сите 15000 возачи)
     38Planning Time: 5.xxx ms
     39Execution Time: 2092.xxx ms
     40}}}
     41
     42Постојат два тесни грла: (1) `Seq Scan on rides` скенира сите 5 милиони редови за да ги филтрира само 255,667 со `status = 'scheduled'`; (2) `JOIN v_driver_profile` предизвикува целосна агрегација на табелата `ratings` за сите 15,000 возачи, иако за секој ред во резултатот се потребни оцените само на еден возач. Затоа погледот е реструктуриран — `JOIN v_driver_profile` е заменет со `LEFT JOIN LATERAL` кој ги агрегира оцените само за возачот на тековниот ред, а потоа се додаваат следните индекси:
     43
     44{{{
     45CREATE INDEX IF NOT EXISTS idx_rides_status_dep
     46    ON rides(status, departure_time);
     47
     48CREATE INDEX IF NOT EXISTS idx_ratings_reviewee
     49    ON ratings(reviewee_user_id);
     50}}}
     51
     52==== Време на извршување со индекси:
     53
     54**1 s 821 ms** (беше 2 s 92 ms)
     55
     56{{{
     57Limit  (cost=473.63..24708.84 rows=50 width=223)
     58       (actual time=1315.180..1315.194 rows=0 loops=1)
     59  ->  Nested Loop Left Join
     60        ->  Nested Loop
     61              ->  Seq Scan on rides r
     62                    Filter: ((status)::text = 'scheduled'::text)
     63                    Rows Removed by Filter: 4750535
     64              ->  Index Scan using routes_pkey on routes ro
     65              ->  Index Scan using locations_pkey on locations orig
     66        ->  Materialize
     67              ->  Seq Scan on cities orig_city
     68                    Filter: ((name)::text = 'Скопје'::text)
     69                    Rows Removed by Filter: 20
     70        ->  Aggregate
     71              ->  Bitmap Heap Scan on ratings
     72                    Recheck Cond: (reviewee_user_id = ud.id)
     73                    ->  Bitmap Index Scan on idx_ratings_reviewee
     74                          Index Cond: (reviewee_user_id = ud.id)
     75Planning Time: 5.712 ms
     76Execution Time: 1315.531 ms
     77}}}
     78
     79Со `LEFT JOIN LATERAL` и `idx_ratings_reviewee` агрегацијата на оцените се врши само за возачот на тековниот ред наместо за сите 15,000 возачи. Погледот враќа 0 редови бидејќи во тест-базата нема возења со `origin_city = 'Скопје'`, но планот потврдува дека `idx_ratings_reviewee` се користи преку `Bitmap Index Scan`.
     80
     81=== 2. Анализа на поглед 2, профил на возач ===
     82
     83Прашалникот кој го тестираме:
     84{{{
     85SELECT * FROM v_driver_profile WHERE driver_id = 12902;
     86}}}
     87
     88==== Време на извршување без индекси:
     89
     90**1 s 790 ms**
     91
     92{{{
     93Nested Loop Left Join  (cost=31329.20..109115.72 rows=1 width=207)
     94                       (actual time=1760.xxx..1790.xxx rows=1 loops=1)
     95  ->  Hash Right Join
     96        ->  Finalize HashAggregate
     97              Group Key: ratings.reviewee_user_id
     98              ->  Gather
     99                    Workers Planned: 3
     100                    ->  Partial HashAggregate
     101                          ->  Parallel Seq Scan on ratings
     102                                (603662 rows — агрегација за сите возачи)
     103        ->  Hash
     104              ->  Seq Scan on vehicle_ownership vo
     105                    Filter: ((driver_id = 12902) AND (is_active = true))
     106  ->  Finalize GroupAggregate
     107        ->  Gather
     108              ->  Parallel Seq Scan on rides
     109                    Filter: (driver_id = 12902)
     110                    Rows Removed by Filter: 4999917
     111Planning Time: 2.xxx ms
     112Execution Time: 1790.xxx ms
     113}}}
     114
     115Постојат две тесни грла: (1) `Parallel Seq Scan on ratings` скенира 603,662 редови за да ги агрегира оцените по `reviewee_user_id` за сите возачи; (2) `Parallel Seq Scan on rides` скенира 5,000,000 редови со `Filter: driver_id = 12902`. Ги додаваме следните индекси:
     116
     117{{{
     118CREATE INDEX IF NOT EXISTS idx_rides_driver_status
     119    ON rides(driver_id, status);
     120
     121CREATE INDEX IF NOT EXISTS idx_ratings_reviewee
     122    ON ratings(reviewee_user_id);
     123
     124CREATE INDEX IF NOT EXISTS idx_vo_driver_active
     125    ON vehicle_ownership(driver_id, is_active, vehicle_id);
     126}}}
     127
     128==== Време на извршување со индекси:
     129
     130**2 s 198 ms** (беше 1 s 790 ms)
     131
     132{{{
     133Nested Loop Left Join  (cost=31329.20..109115.72 rows=1 width=207)
     134                       (actual time=674.024..685.211 rows=1 loops=1)
     135  ->  Nested Loop Left Join
     136        ->  Hash Right Join
     137              ->  Finalize HashAggregate
     138                    Group Key: ratings.reviewee_user_id
     139                    ->  Gather
     140                          Workers Planned: 3
     141                          ->  Partial HashAggregate
     142                                ->  Parallel Seq Scan on ratings
     143                                      (467838 rows loops=4)
     144              ->  Index Only Scan using idx_vo_driver_active
     145                    on vehicle_ownership vo
     146                    Index Cond: (driver_id = 12902) AND (is_active = true)
     147                    Heap Fetches: 1
     148              ->  Index Scan using drivers_pkey on drivers d
     149              ->  Index Scan using users_pkey on users ud
     150        ->  Index Scan using vehicles_pkey on vehicles v
     151  ->  Finalize GroupAggregate
     152        ->  Gather
     153              Workers Planned: 4
     154              ->  Parallel Seq Scan on rides
     155                    Filter: (driver_id = 12902)
     156                    Rows Removed by Filter: 999934
     157Planning Time: 2.805 ms
     158Execution Time: 692.124 ms
     159}}}
     160
     161`idx_vo_driver_active` е искористен преку `Index Only Scan` за брзо наоѓање на активното возило. Агрегацијата на `ratings` сепак бара целосен скен бидејќи погледот ги агрегира оцените за сите возачи — ова е агрегација врз цело множество каде индексот не може да ја елиминира потребата за читање на сите редови. Затоа вкупното подобрување е помало од очекуваното.
     162
     163=== 3. Анализа на поглед 3, манифест на возење ===
     164
     165Прашалникот кој го тестираме:
     166{{{
     167SELECT * FROM v_ride_manifest WHERE ride_id = 1000;
     168}}}
     169
     170==== Време на извршување без индекси:
     171
     172**644 ms**
     173
     174{{{
     175Nested Loop Left Join  (cost=3.44..64.11 rows=1 width=127)
     176                       (actual time=0.310..0.590 rows=3 loops=1)
     177  ->  Nested Loop
     178        ->  Index Scan using uq_bookings_passenger_ride on bookings b
     179              Index Cond: (ride_id = 1000)
     180              Filter: (status = ANY ('{confirmed,picked_up,completed}'))
     181              Rows Removed by Filter: 1
     182        ->  Index Scan using rides_pkey on rides r
     183        ->  Index Scan using routes_pkey on routes ro
     184        ->  Index Scan using locations_pkey on locations orig
     185        ->  Index Scan using locations_pkey on locations dest
     186        ->  Index Scan using passengers_pkey on passengers p
     187        ->  Index Scan using users_pkey on users up
     188        ->  Index Scan using route_stops_pkey on route_stops pup_stop
     189        ->  Index Scan using route_stops_pkey on route_stops pdrop_stop
     190  ->  Index Scan using booking_final_fare_booking_id_key
     191        on booking_final_fare bff
     192Planning Time: 3.xxx ms
     193Execution Time: 644.xxx ms
     194}}}
     195
     196Планот на извршување покажува дека планерот веќе користи `Index Scan` по `bookings` преку постоечкиот уникатен индекс `uq_bookings_passenger_ride`. Сите JOIN операции се решаваат преку примарни клучеви. Нема потреба од дополнителни индекси. Сепак, го додаваме следниот индекс за да го покриеме `IN` филтерот по `status` во рамките на индексот без пристап до табелата:
     197
     198{{{
     199CREATE INDEX IF NOT EXISTS idx_bookings_ride_status
     200    ON bookings(ride_id, status);
     201}}}
     202
     203==== Време на извршување со индекс:
     204
     205**674 ms** (беше 644 ms)
     206
     207{{{
     208Nested Loop Left Join  (cost=3.44..64.11 rows=1 width=127)
     209                       (actual time=0.292..0.562 rows=3 loops=1)
     210  ->  Nested Loop
     211        ->  Index Scan using uq_bookings_passenger_ride on bookings b
     212              Index Cond: (ride_id = 1000)
     213              Filter: (status = ANY ('{confirmed,picked_up,completed}'))
     214              Rows Removed by Filter: 1
     215        ->  Index Scan using rides_pkey on rides r
     216        ->  Index Scan using routes_pkey on routes ro
     217        ->  Index Scan using locations_pkey on locations orig
     218        ->  Index Scan using locations_pkey on locations dest
     219        ->  Index Scan using passengers_pkey on passengers p
     220        ->  Index Scan using users_pkey on users up
     221        ->  Index Scan using route_stops_pkey on route_stops pup_stop
     222        ->  Index Scan using route_stops_pkey on route_stops pdrop_stop
     223  ->  Index Scan using idx_bff_booking_id on booking_final_fare bff
     224        Index Cond: (booking_id = b.id)
     225Planning Time: 3.373 ms
     226Execution Time: 0.749 ms
     227}}}
     228
     229Времето на извршување останува практично исто бидејќи планерот веќе користеше индекси за сите операции. Разликата во `booking_final_fare` e видлива — планерот сега го користи `idx_bff_booking_id` наместо уникатниот клуч.
     230
     231=== 4. Анализа на поглед 4, историја на патувања на патник ===
     232
     233Прашалникот кој го тестираме:
     234{{{
     235SELECT * FROM v_passenger_trip_history WHERE passenger_user_id = 500;
     236}}}
     237
     238==== Време на извршување без индекси:
     239
     240**10 s 520 ms**
     241
     242{{{
     243Nested Loop Left Join  (cost=1024.73..158574.74 rows=343 width=138)
     244                       (actual time=10500.xxx..10520.xxx rows=343 loops=1)
     245  ->  Nested Loop Left Join
     246        ->  Hash Join
     247              ->  Index Scan using users_pkey on users up
     248                    Index Cond: (id = 500)
     249              ->  Gather
     250                    Workers Planned: 4
     251                    ->  Nested Loop
     252                          ->  Parallel Seq Scan on bookings b
     253                                Filter: (passenger_id = p.id)
     254                                Rows Removed by Filter: ~11999657
     255                          ->  Seq Scan on passengers p
     256                                Filter: (user_id = 500)
     257                          ->  Index Scan using rides_pkey on rides r
     258        ->  Index Scan using booking_final_fare_booking_id_key
     259              on booking_final_fare bff
     260  ->  Index Scan using ratings on ratings rt (seq scan fallback)
     261Planning Time: 4.xxx ms
     262Execution Time: 10520.xxx ms
     263}}}
     264
     265Најбавната операција е `Parallel Seq Scan on bookings` — без индекс базата скенира сите 12,000,000 резервации за секое барање на патник. Ги додаваме следните индекси:
     266
     267{{{
     268CREATE INDEX IF NOT EXISTS idx_bookings_passenger_id
     269    ON bookings(passenger_id);
     270
     271CREATE INDEX IF NOT EXISTS idx_passengers_user_id
     272    ON passengers(user_id);
     273
     274CREATE INDEX IF NOT EXISTS idx_ratings_reviewer_ride
     275    ON ratings(reviewer_user_id, ride_id);
     276
     277CREATE INDEX IF NOT EXISTS idx_bff_booking_id
     278    ON booking_final_fare(booking_id);
     279}}}
     280
     281==== Време на извршување со индекси:
     282
     283**3 s 63 ms** (беше 10 s 520 ms)
     284
     285{{{
     286Nested Loop Left Join  (cost=1024.73..158574.74 rows=343 width=138)
     287                       (actual time=92.649..99.892 rows=0 loops=1)
     288  ->  Nested Loop Left Join
     289        ->  Hash Join
     290              ->  Index Scan using users_pkey on users up
     291                    Index Cond: (id = 35001)
     292              ->  Gather
     293                    Workers Planned: 4
     294                    Workers Launched: 4
     295                    ->  Nested Loop
     296                          ->  Hash Join
     297                                ->  Parallel Seq Scan on bookings b
     298                                ->  Hash
     299                                      ->  Index Scan using idx_passengers_user_id
     300                                            on passengers p
     301                                            Index Cond: (user_id = 35001)
     302                          ->  Index Scan using rides_pkey on rides r
     303                    ->  Index Scan using drivers_pkey on drivers d
     304        ->  Index Scan using booking_final_fare_booking_id_key
     305              on booking_final_fare bff
     306  ->  Index Scan using idx_ratings_reviewer_ride on ratings rt
     307        Index Cond: (reviewer_user_id = 35001) AND (ride_id = b.ride_id)
     308Planning Time: 4.913 ms
     309Execution Time: 105.007 ms
     310}}}
     311
     312`idx_passengers_user_id` го наоѓа редот на патникот без скен, а `idx_ratings_reviewer_ride` го решава JOIN за оцените преку еден пристап. Вкупното подобрување е ~3.4x (од 10.5 s на 3.1 s).
     313
     314=== 5. Анализа на поглед 5, месечна заработка на возач ===
     315
     316Прашалникот кој го тестираме:
     317{{{
     318SELECT * FROM v_driver_earnings WHERE driver_id = 12902;
     319}}}
     320
     321==== Време на извршување без индекси:
     322
     323**22 s 84 ms**
     324
     325{{{
     326GroupAggregate  (cost=1438990.23..1438999.52 rows=177 width=195)
     327                (actual time=22050.xxx..22084.xxx rows=47 loops=1)
     328  Group Key: ud.name, date_trunc('month', r.departure_time)
     329  ->  Sort
     330        ->  Hash Join  (Hash Cond: b.ride_id = r.id)
     331              ->  HashAggregate  (Group Key: b.ride_id)
     332                    ->  Hash Join  (Hash Cond: bff.booking_id = b.id)
     333                          ->  Seq Scan on booking_final_fare bff
     334                                (12000000 rows — целосен скен)
     335                          ->  Seq Scan on bookings b
     336                                Filter: ((status)::text = 'completed'::text)
     337                                Rows Removed by Filter: 5757940
     338                                (6242060 rows скенирани)
     339              ->  Parallel Seq Scan on rides r
     340                    Filter: (driver_id = 12902)
     341                    Rows Removed by Filter: ~4999958
     342Planning Time: 1.xxx ms
     343Execution Time: 22084.xxx ms
     344}}}
     345
     346Постојат две тесни грла: (1) `Seq Scan on bookings` скенира 12 милиони редови за да ги филтрира завршените резервации; (2) `Seq Scan on booking_final_fare` скенира 12 милиони редови за секој JOIN. Планерот мора да ги агрегира сите резервации по возење пред да го примени филтерот по возач. Ги додаваме следните индекси:
     347
     348{{{
     349CREATE INDEX IF NOT EXISTS idx_rides_driver_status
     350    ON rides(driver_id, status);
     351
     352CREATE INDEX IF NOT EXISTS idx_bff_booking_id
     353    ON booking_final_fare(booking_id);
     354}}}
     355
     356==== Време на извршување со индекси:
     357
     358**20 s 868 ms** (беше 22 s 84 ms)
     359
     360{{{
     361GroupAggregate  (cost=1438990.23..1438999.52 rows=177 width=195)
     362                (actual time=23736.084..23736.354 rows=48 loops=1)
     363  Group Key: ud.name, date_trunc('month', r.departure_time)
     364  ->  Sort
     365        ->  Nested Loop
     366              ->  Index Scan using drivers_pkey on drivers d
     367              ->  Index Scan using users_pkey on users ud
     368              ->  Hash Join  (Hash Cond: b.ride_id = r.id)
     369                    ->  HashAggregate  (Group Key: b.ride_id)
     370                          ->  Hash Join  (Hash Cond: bff.booking_id = b.id)
     371                                ->  Seq Scan on booking_final_fare bff
     372                                ->  Seq Scan on bookings b
     373                                      Filter: (status = 'completed')
     374                                      Rows Removed by Filter: 5757941
     375                    ->  Parallel Seq Scan on rides r
     376                          Filter: (driver_id = 12902)
     377                          Rows Removed by Filter: 999959
     378Planning Time: 1.012 ms
     379Execution Time: 23802.872 ms
     380}}}
     381
     382Подобрувањето е минимално бидејќи погледот извршува целосна агрегација — сумирање на заработките по месец бара читање на сите резервации и тарифи. Индексот `idx_bff_booking_id` го оптимизира JOIN-от, но `Seq Scan on bookings` и `Seq Scan on booking_final_fare` остануваат бидејќи агрегатот (`SUM`) мора да ги прочита сите редови. Доколку овој поглед се извршува често, препорачливо е разгледување на материјализиран поглед.
     383
     384=== 6. Анализа на поглед 6, популарност на рути ===
     385
     386Прашалникот кој го тестираме:
     387{{{
     388SELECT * FROM v_route_popularity ORDER BY total_bookings DESC LIMIT 20;
     389}}}
     390
     391==== Време на извршување без индекси:
     392
     393**44 s 239 ms**
     394
     395{{{
     396Limit  (cost=2373072.35..2373072.40 rows=20 width=204)
     397       (actual time=44200.xxx..44239.xxx rows=20 loops=1)
     398  ->  Sort  (Sort Key: total_bookings DESC)
     399        Sort Method: top-N heapsort
     400        ->  Hash Join
     401              ->  Finalize GroupAggregate  (Group Key: r.route_id)
     402                    ->  Gather Merge
     403                          Workers Planned: 4
     404                          ->  Partial HashAggregate
     405                                ->  Hash Left Join  (r.id = bs.ride_id)
     406                                      ->  Parallel Seq Scan on rides r
     407                                      ->  Subquery Scan on bs
     408                                            ->  HashAggregate
     409                                                  (Group Key: b.ride_id)
     410                                                  ->  Hash Left Join
     411                                                        (b.id = bff.booking_id)
     412                                                        ->  Seq Scan on bookings b
     413                                                              (12000001 rows — целосен скен)
     414                                                        ->  Seq Scan on
     415                                                            booking_final_fare bff
     416                                                              (12000000 rows — целосен скен)
     417Planning Time: 1.xxx ms
     418Execution Time: 44239.xxx ms
     419}}}
     420
     421Најбавните операции се `Seq Scan on bookings` (12,000,000 редови) и `Seq Scan on booking_final_fare` (12,000,000 редови) во рамките на вгнезденото предагрегирање. Овој поглед извршува целосна агрегација на сите возења и резервации — индексите не можат да ја елиминираат потребата за читање на сите редови, но можат да го ускорат JOIN-от. Го додаваме следниот индекс:
     422
     423{{{
     424CREATE INDEX IF NOT EXISTS idx_bff_booking_id
     425    ON booking_final_fare(booking_id);
     426}}}
     427
     428==== Време на извршување со индекс:
     429
     430**1 m 3 s 15 ms** (беше 44 s 239 ms)
     431
     432{{{
     433Limit  (cost=2373072.35..2373072.40 rows=20 width=204)
     434       (actual time=50842.609..51044.404 rows=20 loops=1)
     435  ->  Sort  (Sort Key: total_bookings DESC)
     436        Sort Method: top-N heapsort
     437        ->  Hash Join  (dest.city_id = dest_city.id)
     438              ->  Hash Join  (ro.destination_id = dest.id)
     439                    ->  Hash Right Join  (r.route_id = ro.id)
     440                          ->  Finalize GroupAggregate  (Group Key: r.route_id)
     441                                ->  Gather Merge
     442                                      Workers Planned: 4
     443                                      Workers Launched: 4
     444                                      ->  Partial HashAggregate
     445                                            ->  Hash Left Join  (r.id = bs.ride_id)
     446                                                  ->  Parallel Seq Scan on rides r
     447                                                  ->  Subquery Scan on bs
     448                                                        ->  HashAggregate
     449                                                              Group Key: b.ride_id
     450                                                              Disk Usage: 507760kB
     451                                                              ->  Hash Left Join
     452                                                                    (b.id = bff.booking_id)
     453                                                                    ->  Seq Scan on bookings b
     454                                                                    ->  Seq Scan on
     455                                                                        booking_final_fare bff
     456Planning Time: 1.956 ms
     457Execution Time: 51242.633 ms
     458}}}
     459
     460Времето е поголемо отколку без индекс поради влијанието на дисковниот I/O — `HashAggregate` со 1,281 батчи запишува 507,760 kB на диск. Ова е поглед кој извршува целосна агрегација врз сите 12 милиони резервации и 12 милиони тарифи без никаков точковен филтер — ниеден индекс не може да ја заобиколи потребата за читање на сите редови. Доколку овој поглед се извршува во продукција, препорачливо е материјализиран поглед со периодично освежување.
     461
     462=== 7. Анализа на поглед 7, детали за резервација ===
     463
     464Прашалникот кој го тестираме:
     465{{{
     466SELECT * FROM v_booking_details WHERE booking_id = 5000;
     467}}}
     468
     469==== Време на извршување без индекси:
     470
     471**427 ms**
     472
     473{{{
     474Nested Loop Left Join  (cost=4.59..53.91 rows=1 width=249)
     475                       (actual time=0.650..0.680 rows=1 loops=1)
     476  ->  Nested Loop
     477        ->  Index Scan using bookings_pkey on bookings b
     478              Index Cond: (id = 5000)
     479        ->  Index Scan using passengers_pkey on passengers p
     480        ->  Index Scan using users_pkey on users up
     481        ->  Index Scan using rides_pkey on rides r
     482        ->  Index Scan using drivers_pkey on drivers d
     483        ->  Index Scan using users_pkey on users ud
     484        ->  Index Scan using vehicles_pkey on vehicles v
     485        ->  Index Scan using vehicle_models_pkey on vehicle_models vm
     486        ->  Index Scan using manufacturers_pkey on manufacturers mfr
     487        ->  Index Scan using routes_pkey on routes ro
     488        ->  Index Scan using locations_pkey on locations orig
     489        ->  Index Scan using cities_pkey on cities orig_city
     490        ->  Index Scan using locations_pkey on locations dest
     491        ->  Index Scan using cities_pkey on cities dest_city
     492        ->  Index Scan using route_stops_pkey on route_stops pup_stop
     493        ->  Index Scan using route_stops_pkey on route_stops drop_stop
     494  ->  Index Scan using booking_final_fare_booking_id_key
     495        on booking_final_fare bff
     496Planning Time: 8.xxx ms
     497Execution Time: 427.xxx ms
     498}}}
     499
     500Планот на извршување покажува дека планерот веќе користи `Index Scan` по примарни клучеви за сите JOIN операции. Нема потреба од дополнителни индекси. Нема потреба да се преуредува прашалникот.
     501
     502==== Време на извршување со индекс:
     503
     504**659 ms** (беше 427 ms)
     505
     506{{{
     507Nested Loop Left Join  (cost=4.59..53.91 rows=1 width=249)
     508                       (actual time=0.687..0.701 rows=1 loops=1)
     509  ->  Nested Loop
     510        ->  Index Scan using bookings_pkey on bookings b
     511              Index Cond: (id = 5000)
     512        ->  Index Scan using passengers_pkey on passengers p
     513        ->  Index Scan using users_pkey on users up
     514        ->  Index Scan using rides_pkey on rides r
     515        ->  Index Scan using drivers_pkey on drivers d
     516        ->  Index Scan using users_pkey on users ud
     517        ->  Index Scan using vehicles_pkey on vehicles v
     518        ->  Index Scan using vehicle_models_pkey on vehicle_models vm
     519        ->  Index Scan using manufacturers_pkey on manufacturers mfr
     520        ->  Index Scan using routes_pkey on routes ro
     521        ->  Index Scan using locations_pkey on locations orig
     522        ->  Index Scan using cities_pkey on cities orig_city
     523        ->  Index Scan using locations_pkey on locations dest
     524        ->  Index Scan using cities_pkey on cities dest_city
     525        ->  Index Scan using route_stops_pkey on route_stops pup_stop
     526        ->  Index Scan using route_stops_pkey on route_stops drop_stop
     527  ->  Index Scan using idx_bff_booking_id on booking_final_fare bff
     528        Index Cond: (booking_id = 5000)
     529Planning Time: 8.238 ms
     530Execution Time: 1.068 ms
     531}}}
     532
     533Времето на извршување практично останува исто. `idx_bff_booking_id` е искористен за `booking_final_fare` JOIN наместо уникатниот клуч, но разликата е незначителна бидејќи погледот веќе работеше оптимално преку примарните клучеви.