| | 1 | = Преглед: v_orders_full = |
| | 2 | |
| | 3 | ||= Датотека ||= `views/02_orders_full_view.sql` || |
| | 4 | ||= Шема ||= `kbnteam` || |
| | 5 | ||= Категорија ||= Нарачки и Достави || |
| | 6 | ||= Поврзани индекси ||= `indexes/v_orders_full_index.sql` || |
| | 7 | ||= Сложеност ||= Висока — 2 CTE + 12 табели || |
| | 8 | |
| | 9 | == Опис == |
| | 10 | Примарен оперативен преглед за нарачки во веб апликацијата. Ги прикажува целосните детали за нарачка: контекст на купувач/компанија, статус на нарачка, контекст на достава и агрегирани листи на нарачани оброци и пијачи. Двете CTE (`order_meals`, `order_drinks`) ги собираат ставките по нарачка во читливи стрингови. |
| | 11 | |
| | 12 | '''Важно:''' Овој преглед е еден од најтешките во шемата. Никогаш не го извршувајте со `SELECT *` без WHERE клаузула при бенчмарк тестирање. |
| | 13 | |
| | 14 | == Зависности == |
| | 15 | ||= Табела ||= Тип на употреба || |
| | 16 | || `kbnteam.customer_order` || Главна табела (нарачка) || |
| | 17 | || `kbnteam.order_status` || JOIN — статус на нарачката || |
| | 18 | || `kbnteam.customer` || JOIN — купувач || |
| | 19 | || `kbnteam.api_user` || JOIN (×2) — детали за купувач и возач || |
| | 20 | || `kbnteam.company_order` || JOIN — компаниска нарачка || |
| | 21 | || `kbnteam.company` || JOIN — компанија || |
| | 22 | || `kbnteam.delivery` || LEFT JOIN — достава || |
| | 23 | || `kbnteam.delivery_status` || LEFT JOIN — статус на достава || |
| | 24 | || `kbnteam.driver` || LEFT JOIN — возач || |
| | 25 | || `kbnteam.order_meal` || LEFT JOIN (CTE) — оброци во нарачката || |
| | 26 | || `kbnteam.meal` || JOIN (CTE) — назив на оброк || |
| | 27 | || `kbnteam.order_drink` || LEFT JOIN (CTE) — пијачи во нарачката || |
| | 28 | || `kbnteam.drink` || JOIN (CTE) — назив на пијач || |
| | 29 | |
| | 30 | == Излезни колони == |
| | 31 | ||= Колона ||= Извор ||= Опис || |
| | 32 | || `order_id` || `customer_order` || Примарен клуч на нарачката || |
| | 33 | || `order_datetime` || `customer_order` || Датум и час на нарачката || |
| | 34 | || `order_total` || `customer_order` || Вкупна цена || |
| | 35 | || `o_status_name` || `order_status` || Статус на нарачката || |
| | 36 | || `customer_user_id` || `customer` || ID на купувачот || |
| | 37 | || `customer_company_id` || `customer` || ID на компанијата на купувачот || |
| | 38 | || `customer_first_name` || `api_user` || Ime на купувачот || |
| | 39 | || `customer_last_name` || `api_user` || Презиме на купувачот || |
| | 40 | || `customer_email` || `api_user` || Е-пошта на купувачот || |
| | 41 | || `comp_order_id` || `company_order` || ID на компаниската нарачка || |
| | 42 | || `order_company_id` || `company_order` || ID на компанијата || |
| | 43 | || `company_name` || `company` || Назив на компанијата || |
| | 44 | || `delivery_id` || `delivery` || ID на доставата (NULL ако нема) || |
| | 45 | || `delivery_date` || `delivery` || Датум на достава || |
| | 46 | || `d_status_name` || `delivery_status` || Статус на достава || |
| | 47 | || `driver_user_id` || `driver` || ID на возачот || |
| | 48 | || `driver_first_name` || `api_user` || Ime на возачот || |
| | 49 | || `driver_last_name` || `api_user` || Презиме на возачот || |
| | 50 | || `driver_phone` || `api_user` || Телефон на возачот || |
| | 51 | || `meals` || CTE `order_meals` || Оброци одвоени со запирка || |
| | 52 | || `drinks` || CTE `order_drinks` || Пијачи одвоени со запирка || |
| | 53 | |
| | 54 | == SQL Дефиниција == |
| | 55 | {{{ |
| | 56 | #!sql |
| | 57 | CREATE OR REPLACE VIEW kbnteam.v_orders_full AS |
| | 58 | WITH order_meals AS ( |
| | 59 | SELECT |
| | 60 | x.order_id, |
| | 61 | string_agg(x.meal_name, ', ' ORDER BY x.meal_name) AS meals |
| | 62 | FROM ( |
| | 63 | SELECT DISTINCT |
| | 64 | om.order_id, |
| | 65 | m.meal_name |
| | 66 | FROM kbnteam.order_meal om |
| | 67 | JOIN kbnteam.meal m ON m.meal_id = om.meal_id |
| | 68 | ) x |
| | 69 | GROUP BY x.order_id |
| | 70 | ), |
| | 71 | order_drinks AS ( |
| | 72 | SELECT |
| | 73 | x.order_id, |
| | 74 | string_agg(x.drink_name, ', ' ORDER BY x.drink_name) AS drinks |
| | 75 | FROM ( |
| | 76 | SELECT DISTINCT |
| | 77 | od.order_id, |
| | 78 | d.drink_name |
| | 79 | FROM kbnteam.order_drink od |
| | 80 | JOIN kbnteam.drink d ON d.drink_id = od.drink_id |
| | 81 | ) x |
| | 82 | GROUP BY x.order_id |
| | 83 | ) |
| | 84 | SELECT |
| | 85 | o.order_id, |
| | 86 | o.order_datetime, |
| | 87 | o.order_total, |
| | 88 | os.o_status_name, |
| | 89 | cu.user_id AS customer_user_id, |
| | 90 | cu.company_id AS customer_company_id, |
| | 91 | au.user_first_name AS customer_first_name, |
| | 92 | au.user_last_name AS customer_last_name, |
| | 93 | au.user_email AS customer_email, |
| | 94 | co.comp_order_id, |
| | 95 | co.company_id AS order_company_id, |
| | 96 | cmp.company_name, |
| | 97 | d.delivery_id, |
| | 98 | d.delivery_date, |
| | 99 | ds.d_status_name, |
| | 100 | drv.user_id AS driver_user_id, |
| | 101 | du.user_first_name AS driver_first_name, |
| | 102 | du.user_last_name AS driver_last_name, |
| | 103 | du.user_phone_no AS driver_phone, |
| | 104 | COALESCE(om.meals, '') AS meals, |
| | 105 | COALESCE(od.drinks, '') AS drinks |
| | 106 | FROM kbnteam.customer_order o |
| | 107 | JOIN kbnteam.order_status os ON os.o_status_id = o.o_status_id |
| | 108 | JOIN kbnteam.customer cu ON cu.user_id = o.customer_user_id |
| | 109 | JOIN kbnteam.api_user au ON au.user_id = cu.user_id |
| | 110 | JOIN kbnteam.company_order co ON co.comp_order_id = o.comp_order_id |
| | 111 | JOIN kbnteam.company cmp ON cmp.company_id = co.company_id |
| | 112 | LEFT JOIN kbnteam.delivery d ON d.delivery_id = co.delivery_id |
| | 113 | LEFT JOIN kbnteam.delivery_status ds ON ds.d_status_id = d.d_status_id |
| | 114 | LEFT JOIN kbnteam.driver drv ON drv.user_id = d.driver_user_id |
| | 115 | LEFT JOIN kbnteam.api_user du ON du.user_id = drv.user_id |
| | 116 | LEFT JOIN order_meals om ON om.order_id = o.order_id |
| | 117 | LEFT JOIN order_drinks od ON od.order_id = o.order_id; |
| | 118 | }}} |
| | 119 | |
| | 120 | == Тестирање на перформанси == |
| | 121 | |
| | 122 | === Препорачано тест прашање === |
| | 123 | {{{ |
| | 124 | #!sql |
| | 125 | SET search_path TO kbnteam; |
| | 126 | SET statement_timeout = '60s'; |
| | 127 | |
| | 128 | -- Тест 1: последни 30 дена (временски опсег — тестира idx по datetime) |
| | 129 | EXPLAIN (ANALYZE, BUFFERS, VERBOSE) |
| | 130 | SELECT * FROM kbnteam.v_orders_full |
| | 131 | WHERE order_datetime >= CURRENT_DATE - INTERVAL '30 days'; |
| | 132 | |
| | 133 | -- Тест 2: по купувач (тестира idx по customer_user_id) |
| | 134 | EXPLAIN (ANALYZE, BUFFERS, VERBOSE) |
| | 135 | SELECT * FROM kbnteam.v_orders_full |
| | 136 | WHERE customer_user_id = 1; |
| | 137 | |
| | 138 | -- Тест 3: по компаниска нарачка (тестира idx по comp_order_id) |
| | 139 | EXPLAIN (ANALYZE, BUFFERS, VERBOSE) |
| | 140 | SELECT * FROM kbnteam.v_orders_full |
| | 141 | WHERE comp_order_id = 1; |
| | 142 | }}} |
| | 143 | |
| | 144 | === Резултати пред индексирање === |
| | 145 | Извршете ги горните прашања '''пред''' да ја примените датотеката `indexes/v_orders_full_index.sql`. |
| | 146 | |
| | 147 | ||= Метрика ||= Тест 1 (datetime) ||= Тест 2 (customer) ||= Тест 3 (comp_order) || |
| | 148 | || Planning Time || ___ ms || ___ ms || ___ ms || |
| | 149 | || Execution Time || ___ ms || ___ ms || ___ ms || |
| | 150 | || Rows Returned || ___ || ___ || ___ || |
| | 151 | || customer_order scan || ___ || ___ || ___ || |
| | 152 | || order_meal scan || ___ || ___ || ___ || |
| | 153 | || delivery scan || ___ || ___ || ___ || |
| | 154 | |
| | 155 | {{{ |
| | 156 | -- Излезот од EXPLAIN ANALYZE овде (пред индексирање) |
| | 157 | }}} |
| | 158 | |
| | 159 | === Применети индекси === |
| | 160 | {{{ |
| | 161 | #!sql |
| | 162 | -- indexes/v_orders_full_index.sql |
| | 163 | CREATE INDEX IF NOT EXISTS idx_customer_order_comp_order_id |
| | 164 | ON kbnteam.customer_order (comp_order_id); |
| | 165 | |
| | 166 | CREATE INDEX IF NOT EXISTS idx_customer_order_customer_user_id_order_datetime |
| | 167 | ON kbnteam.customer_order (customer_user_id, order_datetime DESC); |
| | 168 | |
| | 169 | CREATE INDEX IF NOT EXISTS idx_delivery_driver_user_id_delivery_date |
| | 170 | ON kbnteam.delivery (driver_user_id, delivery_date); |
| | 171 | |
| | 172 | CREATE INDEX IF NOT EXISTS idx_order_meal_order_id_meal_id |
| | 173 | ON kbnteam.order_meal (order_id, meal_id); |
| | 174 | |
| | 175 | CREATE INDEX IF NOT EXISTS idx_order_drink_order_id_drink_id |
| | 176 | ON kbnteam.order_drink (order_id, drink_id); |
| | 177 | }}} |
| | 178 | |
| | 179 | === Резултати по индексирање === |
| | 180 | Извршете ги истите прашања '''по''' примена на `indexes/v_orders_full_index.sql`. |
| | 181 | |
| | 182 | ||= Метрика ||= Тест 1 (datetime) ||= Тест 2 (customer) ||= Тест 3 (comp_order) || |
| | 183 | || Planning Time || ___ ms || ___ ms || ___ ms || |
| | 184 | || Execution Time || ___ ms || ___ ms || ___ ms || |
| | 185 | || Rows Returned || ___ || ___ || ___ || |
| | 186 | || customer_order scan || ___ || ___ || ___ || |
| | 187 | || order_meal scan || ___ || ___ || ___ || |
| | 188 | || delivery scan || ___ || ___ || ___ || |
| | 189 | |
| | 190 | {{{ |
| | 191 | -- Излезот од EXPLAIN ANALYZE овде (по индексирање) |
| | 192 | }}} |
| | 193 | |
| | 194 | === Анализа на подобрување === |
| | 195 | ||= Индекс ||= Помага на ||= Очекувана промена || |
| | 196 | || `idx_customer_order_customer_user_id_order_datetime` || Тест 1 и Тест 2 || Seq Scan → Index Scan на `customer_order` || |
| | 197 | || `idx_customer_order_comp_order_id` || Тест 3 и JOIN со `company_order` || Seq Scan → Index Scan на `customer_order` || |
| | 198 | || `idx_order_meal_order_id_meal_id` || CTE `order_meals` || Seq Scan → Index Scan на `order_meal` || |
| | 199 | || `idx_order_drink_order_id_drink_id` || CTE `order_drinks` || Seq Scan → Index Scan на `order_drink` || |
| | 200 | || `idx_delivery_driver_user_id_delivery_date` || LEFT JOIN на `delivery` || Seq Scan → Index Scan на `delivery` || |
| | 201 | |
| | 202 | ||= Метрика ||= Пред ||= По ||= Δ Подобрување || |
| | 203 | || Execution Time (Тест 1) || ___ ms || ___ ms || ___ % || |
| | 204 | || Execution Time (Тест 2) || ___ ms || ___ ms || ___ % || |
| | 205 | || Execution Time (Тест 3) || ___ ms || ___ ms || ___ % || |