| 4 | | == 1. ABC Анализа на производи (ABC Product Analysis) == |
| 5 | | |
| 6 | | Овој извештај ги дели производите во сегменти A (80%), B (15%) и C (5%) според нивниот придонес во вкупниот промет, користејќи ги овие проценти за да се прикаже кои производи се најважни за приходот и да се олесни управувањето со залихите. |
| 7 | | |
| 8 | | {{{#!sql |
| 9 | | WITH ProductRevenue AS ( |
| 10 | | SELECT |
| 11 | | p.name AS product_name, |
| 12 | | SUM(si.quantity * si.unit_price_at_sale) AS total_revenue |
| 13 | | FROM product p |
| 14 | | JOIN sale_item si ON p.product_id = si.product_id |
| 15 | | GROUP BY p.name |
| 16 | | ), |
| 17 | | RevenueCalculations AS ( |
| 18 | | SELECT |
| 19 | | product_name, |
| 20 | | total_revenue, |
| 21 | | SUM(total_revenue) OVER (ORDER BY total_revenue DESC) / SUM(total_revenue) OVER () AS cumulative_share |
| 22 | | FROM ProductRevenue |
| 23 | | ) |
| 24 | | SELECT |
| 25 | | product_name, |
| 26 | | ROUND(total_revenue, 2) AS revenue_mkd, |
| 27 | | CASE |
| 28 | | WHEN cumulative_share <= 0.80 THEN 'A (High Value)' |
| 29 | | WHEN cumulative_share <= 0.95 THEN 'B (Medium Value)' |
| 30 | | ELSE 'C (Low Value)' |
| 31 | | END AS abc_classification |
| 32 | | FROM RevenueCalculations; |
| 33 | | }}} |
| 34 | | |
| 35 | | '''Релациона алгебра:''' |
| 36 | | {{{ |
| 37 | | ProductRevenue ← γ name; SUM(quantity × unit_price_at_sale) → total_revenue (product ⨝ product_id sale_item) |
| 38 | | |
| 39 | | RevenueCalculations ← π product_name, total_revenue, |
| 40 | | (RunningSum(total_revenue) / TotalSum(total_revenue)) → cumulative_share |
| 41 | | (τ total_revenue DESC (ProductRevenue)) |
| 42 | | |
| 43 | | Result ← π product_name, |
| 44 | | ROUND(total_revenue, 2) → revenue_mkd, |
| 45 | | CASE(cumulative_share ≤ 0.80 → 'A', |
| 46 | | cumulative_share ≤ 0.95 → 'B', |
| 47 | | ELSE → 'C') → abc_classification |
| 48 | | (RevenueCalculations) |
| 49 | | }}} |
| 50 | | |
| 51 | | ---- |
| 52 | | |
| 53 | | == 2. Месечен тренд на продажба (Monthly Sales Growth Trend) == |
| 54 | | |
| 55 | | Ги споредува приходите од тековниот месец со претходниот за да го пресмета процентот на раст или пад на бизнисот. |
| 56 | | |
| 57 | | {{{#!sql |
| 58 | | WITH MonthlySales AS ( |
| 59 | | SELECT |
| 60 | | DATE_TRUNC('month', date_time) AS sales_month, |
| 61 | | SUM(total_amount) AS revenue |
| 62 | | FROM sale |
| 63 | | GROUP BY 1 |
| 64 | | ) |
| 65 | | SELECT |
| 66 | | sales_month, |
| 67 | | revenue AS current_month_revenue, |
| 68 | | LAG(revenue) OVER (ORDER BY sales_month) AS previous_month_revenue, |
| 69 | | ROUND(((revenue - LAG(revenue) OVER (ORDER BY sales_month)) / |
| 70 | | NULLIF(LAG(revenue) OVER (ORDER BY sales_month), 0)) * 100, 2) AS growth_percentage |
| 71 | | FROM MonthlySales; |
| 72 | | }}} |
| 73 | | |
| 74 | | '''Релациона алгебра:''' |
| 75 | | {{{ |
| 76 | | MonthlySales ← γ DATE_TRUNC('month', date_time) → sales_month; |
| 77 | | SUM(total_amount) → revenue (sale) |
| 78 | | |
| 79 | | Result ← π sales_month, |
| 80 | | revenue → current_month_revenue, |
| 81 | | LAG(revenue) → previous_month_revenue, |
| 82 | | ROUND(((revenue - LAG(revenue)) / NULLIF(LAG(revenue), 0)) × 100, 2) → growth_percentage |
| 83 | | (τ sales_month ASC (MonthlySales)) |
| 84 | | }}} |
| 85 | | |
| 86 | | ---- |
| 87 | | |
| 88 | | == 3. Состојба на залихи и критични точки (Inventory Health & Reorder Levels) == |
| 89 | | |
| 90 | | Ги идентификува производите што се под минимумот во магацините и дава аларм за итна набавка. |
| 91 | | |
| 92 | | {{{#!sql |
| 93 | | SELECT |
| 94 | | p.name AS product_name, |
| 95 | | w.name AS warehouse_location, |
| 96 | | ws.quantity_on_hand, |
| 97 | | p.reorder_level, |
| 98 | | CASE |
| 99 | | WHEN ws.quantity_on_hand <= p.reorder_level THEN 'CRITICAL: REORDER' |
| 100 | | WHEN ws.quantity_on_hand <= p.reorder_level * 1.5 THEN 'WARNING: LOW' |
| 101 | | ELSE 'HEALTHY' |
| 102 | | END AS stock_status |
| 103 | | FROM product p |
| 104 | | JOIN warehouse_stock ws ON p.product_id = ws.product_id |
| 105 | | JOIN warehouse w ON ws.warehouse_id = w.warehouse_id; |
| 106 | | }}} |
| 107 | | |
| 108 | | '''Релациона алгебра:''' |
| 109 | | {{{ |
| 110 | | Temp1 ← product ⨝ product_id=product_id warehouse_stock |
| 111 | | |
| 112 | | Temp2 ← Temp1 ⨝ warehouse_id=warehouse_id warehouse |
| 113 | | |
| 114 | | Result ← π product.name → product_name, |
| 115 | | warehouse.name → warehouse_location, |
| 116 | | quantity_on_hand, |
| 117 | | reorder_level, |
| 118 | | CASE(quantity_on_hand ≤ reorder_level → 'CRITICAL: REORDER', |
| 119 | | quantity_on_hand ≤ reorder_level × 1.5 → 'WARNING: LOW', |
| 120 | | ELSE → 'HEALTHY') → stock_status |
| 121 | | (Temp2) |
| 122 | | }}} |
| 123 | | |
| 124 | | ---- |
| 125 | | |
| 126 | | == 4. Перформанси на добавувачите (Supplier Performance Matrix) == |
| 127 | | |
| 128 | | Го мери доцнењето на испораките од добавувачите и ја пресметува нивната доверливост во исполнување на роковите. |
| 129 | | |
| 130 | | {{{#!sql |
| 131 | | SELECT |
| 132 | | s.name AS supplier_name, |
| 133 | | COUNT(po.po_id) AS total_orders, |
| 134 | | AVG(po.actual_delivery_date - po.expected_delivery_date) AS average_delay_days, |
| 135 | | ROUND(COUNT(CASE WHEN po.actual_delivery_date <= po.expected_delivery_date THEN 1 END) * 100.0 / COUNT(*), 2) AS on_time_delivery_rate |
| 136 | | FROM supplier s |
| 137 | | JOIN purchase_order po ON s.supplier_id = po.supplier_id |
| 138 | | WHERE po.status = 'DELIVERED' |
| 139 | | GROUP BY s.name; |
| 140 | | }}} |
| 141 | | |
| 142 | | '''Релациона алгебра:''' |
| 143 | | {{{ |
| 144 | | Temp1 ← supplier ⨝ supplier_id=supplier_id purchase_order |
| 145 | | |
| 146 | | Temp2 ← σ status='DELIVERED' (Temp1) |
| 147 | | |
| 148 | | Result ← γ supplier.name → supplier_name; |
| 149 | | COUNT(po_id) → total_orders, |
| 150 | | AVG(actual_delivery_date - expected_delivery_date) → average_delay_days, |
| 151 | | ROUND(COUNT(actual_delivery_date ≤ expected_delivery_date) × 100.0 / COUNT(*), 2) → on_time_delivery_rate |
| 152 | | (Temp2) |
| 153 | | }}} |
| 154 | | |
| 155 | | ---- |
| 156 | | |
| 157 | | == 5. RFM Анализа на клиенти (Customer RFM Segmentation) == |
| 158 | | |
| 159 | | Ги сегментира купувачите според тоа кога последен пат купувале, колку често се враќаат и колку пари потрошиле. |
| 160 | | |
| 161 | | {{{#!sql |
| 162 | | SELECT |
| 163 | | c.name AS customer_name, |
| 164 | | EXTRACT(DAY FROM CURRENT_TIMESTAMP - MAX(s.date_time)) AS recency_days, |
| 165 | | COUNT(s.sale_id) AS frequency_count, |
| 166 | | SUM(s.total_amount) AS monetary_total, |
| 167 | | NTILE(5) OVER (ORDER BY MAX(s.date_time)) AS r_score, |
| 168 | | NTILE(5) OVER (ORDER BY COUNT(s.sale_id)) AS f_score, |
| 169 | | NTILE(5) OVER (ORDER BY SUM(s.total_amount)) AS m_score |
| 170 | | FROM customer c |
| 171 | | JOIN sale s USING (customer_id) |
| 172 | | GROUP BY c.customer_id, c.name; |
| 173 | | }}} |
| 174 | | |
| 175 | | '''Релациона алгебра:''' |
| 176 | | {{{ |
| 177 | | Temp1 ← customer ⨝ customer_id=customer_id sale |
| 178 | | |
| 179 | | Result ← γ customer_id, name → customer_name; |
| 180 | | EXTRACT(DAY FROM CURRENT_TIMESTAMP - MAX(date_time)) → recency_days, |
| 181 | | COUNT(sale_id) → frequency_count, |
| 182 | | SUM(total_amount) → monetary_total, |
| 183 | | NTILE(5, MAX(date_time)) → r_score, |
| 184 | | NTILE(5, COUNT(sale_id)) → f_score, |
| 185 | | NTILE(5, SUM(total_amount)) → m_score |
| 186 | | (Temp1) |
| 187 | | }}} |
| 188 | | |
| 189 | | ---- |
| 190 | | |
| 191 | | == 6. Искористеност на капацитетот на магацините (Warehouse Utilization Rate) == |
| 192 | | |
| 193 | | Пресметува колкав процент од физичкиот простор во секој магацин е моментално пополнет. |
| 194 | | |
| 195 | | {{{#!sql |
| 196 | | SELECT |
| 197 | | w.name AS warehouse_name, |
| 198 | | w.capacity AS total_unit_capacity, |
| 199 | | SUM(ws.quantity_on_hand) AS units_in_stock, |
| 200 | | ROUND((SUM(ws.quantity_on_hand)::NUMERIC / w.capacity) * 100, 2) AS occupancy_percentage |
| 201 | | FROM warehouse w |
| 202 | | LEFT JOIN warehouse_stock ws ON w.warehouse_id = ws.warehouse_id |
| 203 | | GROUP BY w.warehouse_id, w.name, w.capacity; |
| 204 | | }}} |
| 205 | | |
| 206 | | '''Релациона алгебра:''' |
| 207 | | {{{ |
| 208 | | Temp1 ← warehouse ⟕ warehouse_id=warehouse_id warehouse_stock |
| 209 | | |
| 210 | | Result ← γ warehouse_id, name → warehouse_name, capacity → total_unit_capacity; |
| 211 | | SUM(quantity_on_hand) → units_in_stock, |
| 212 | | ROUND((SUM(quantity_on_hand) / capacity) × 100, 2) → occupancy_percentage |
| 213 | | (Temp1) |
| 214 | | }}} |
| 215 | | |
| 216 | | ---- |
| 217 | | |
| 218 | | == 7. Анализа на застојни производи == |
| 219 | | |
| 220 | | Ги прикажува производите што не се продале во последните 90 дена и само зафаќаат простор и капитал. |
| 221 | | |
| 222 | | {{{#!sql |
| 223 | | SELECT |
| 224 | | p.name AS product_name, |
| 225 | | p.sku, |
| 226 | | ws.quantity_on_hand, |
| 227 | | COALESCE(MAX(s.date_time)::TEXT, 'NO SALES RECORDED') AS last_sold_date |
| 228 | | FROM product p |
| 229 | | JOIN warehouse_stock ws ON p.product_id = ws.product_id |
| 230 | | LEFT JOIN sale_item si ON p.product_id = si.product_id |
| 231 | | LEFT JOIN sale s ON si.sale_id = s.sale_id |
| 232 | | GROUP BY p.product_id, p.name, p.sku, ws.quantity_on_hand |
| 233 | | HAVING MAX(s.date_time) < CURRENT_DATE - INTERVAL '90 days' OR MAX(s.date_time) IS NULL; |
| 234 | | }}} |
| 235 | | |
| 236 | | '''Релациона алгебра:''' |
| 237 | | {{{ |
| 238 | | Temp1 ← product ⨝ product_id=product_id warehouse_stock |
| 239 | | |
| 240 | | Temp2 ← Temp1 ⟕ product_id=product_id sale_item |
| 241 | | |
| 242 | | Temp3 ← Temp2 ⟕ sale_id=sale_id sale |
| 243 | | |
| 244 | | Temp4 ← γ product_id, name, sku, quantity_on_hand; |
| 245 | | MAX(date_time) → last_sold_date (Temp3) |
| 246 | | |
| 247 | | Result ← π name → product_name, sku, quantity_on_hand, |
| 248 | | COALESCE(last_sold_date::TEXT, 'NO SALES RECORDED') → last_sold_date |
| 249 | | (σ last_sold_date < CURRENT_DATE - 90 ∨ last_sold_date IS NULL (Temp4)) |
| 250 | | }}} |
| 251 | | |
| 252 | | ---- |
| 253 | | |
| 254 | | == 8. Маржа на профит по категорија (Category Profit Margin) == |
| 255 | | |
| 256 | | Ја пресметува разликата помеѓу продажната и набавната цена за секоја категорија производи. |
| 257 | | |
| 258 | | {{{#!sql |
| 259 | | WITH ProductCosts AS ( |
| 260 | | SELECT product_id, AVG(unit_cost) AS average_cost FROM purchase_order_item GROUP BY 1 |
| 261 | | ) |
| 262 | | SELECT |
| 263 | | c.name AS category_name, |
| 264 | | SUM(si.quantity * si.unit_price_at_sale) AS gross_revenue, |
| 265 | | SUM(si.quantity * pc.average_cost) AS total_cost_of_goods, |
| 266 | | ROUND(((SUM(si.quantity * si.unit_price_at_sale) - SUM(si.quantity * pc.average_cost)) / |
| 267 | | NULLIF(SUM(si.quantity * si.unit_price_at_sale), 0)) * 100, 2) AS profit_margin_pct |
| 268 | | FROM category c |
| 269 | | JOIN product p USING (category_id) |
| 270 | | JOIN sale_item si USING (product_id) |
| 271 | | JOIN ProductCosts pc USING (product_id) |
| 272 | | GROUP BY c.name; |
| 273 | | }}} |
| 274 | | |
| 275 | | '''Релациона алгебра:''' |
| 276 | | {{{ |
| 277 | | ProductCosts ← γ product_id; AVG(unit_cost) → average_cost (purchase_order_item) |
| 278 | | |
| 279 | | Temp1 ← category ⨝ category_id=category_id product |
| 280 | | |
| 281 | | Temp2 ← Temp1 ⨝ product_id=product_id sale_item |
| 282 | | |
| 283 | | Temp3 ← Temp2 ⨝ product_id=product_id ProductCosts |
| 284 | | |
| 285 | | Result ← γ category.name → category_name; |
| 286 | | SUM(quantity × unit_price_at_sale) → gross_revenue, |
| 287 | | SUM(quantity × average_cost) → total_cost_of_goods, |
| 288 | | ROUND(((SUM(quantity × unit_price_at_sale) - SUM(quantity × average_cost)) / |
| 289 | | NULLIF(SUM(quantity × unit_price_at_sale), 0)) × 100, 2) → profit_margin_pct |
| 290 | | (Temp3) |
| 291 | | }}} |
| 292 | | |
| 293 | | ---- |
| 294 | | |
| 295 | | == 9. Анализа на „пазарна кошничка" (Product Affinity / Market Basket) == |
| 296 | | |
| 297 | | Открива кои производи купувачите најчесто ги купуваат заедно во иста трансакција. |
| 298 | | |
| 299 | | {{{#!sql |
| 300 | | SELECT |
| 301 | | p1.name AS product_a, |
| 302 | | p2.name AS product_b, |
| 303 | | COUNT(*) AS pair_occurrence_count |
| 304 | | FROM sale_item si1 |
| 305 | | JOIN sale_item si2 ON si1.sale_id = si2.sale_id AND si1.product_id < si2.product_id |
| 306 | | JOIN product p1 ON si1.product_id = p1.product_id |
| 307 | | JOIN product p2 ON si2.product_id = p2.product_id |
| 308 | | GROUP BY 1, 2 |
| 309 | | ORDER BY pair_occurrence_count DESC |
| 310 | | LIMIT 10; |
| 311 | | }}} |
| 312 | | |
| 313 | | '''Релациона алгебра:''' |
| 314 | | {{{ |
| 315 | | Temp1 ← ρ si1 (sale_item) |
| 316 | | |
| 317 | | Temp2 ← ρ si2 (sale_item) |
| 318 | | |
| 319 | | Temp3 ← σ si1.sale_id = si2.sale_id ∧ si1.product_id < si2.product_id (Temp1 × Temp2) |
| 320 | | |
| 321 | | Temp4 ← Temp3 ⨝ si1.product_id = p1.product_id (ρ p1 (product)) |
| 322 | | |
| 323 | | Temp5 ← Temp4 ⨝ si2.product_id = p2.product_id (ρ p2 (product)) |
| 324 | | |
| 325 | | Temp6 ← γ p1.name → product_a, p2.name → product_b; |
| 326 | | COUNT(*) → pair_occurrence_count (Temp5) |
| 327 | | |
| 328 | | Result ← δ 10 (τ pair_occurrence_count DESC (Temp6)) |
| 329 | | }}} |
| 330 | | |
| 331 | | ---- |
| 332 | | |
| 333 | | == 10. Финансиска вредност на залихите (Stock Asset Valuation) == |
| 334 | | |
| 335 | | Дава точен преглед на вкупната парична вредност на производите во магацините врз основа на последните набавни цени. |
| 336 | | |
| 337 | | {{{#!sql |
| 338 | | SELECT |
| 339 | | w.name AS warehouse_location, |
| 340 | | SUM(ws.quantity_on_hand * ( |
| 341 | | SELECT unit_cost FROM purchase_order_item poi |
| 342 | | WHERE poi.product_id = ws.product_id |
| 343 | | ORDER BY po_id DESC LIMIT 1 |
| 344 | | )) AS total_inventory_valuation_mkd |
| 345 | | FROM warehouse w |
| 346 | | JOIN warehouse_stock ws USING (warehouse_id) |
| 347 | | GROUP BY w.name; |
| 348 | | }}} |
| 349 | | |
| 350 | | '''Релациона алгебра:''' |
| 351 | | {{{ |
| 352 | | LatestCost(product_id) ← π product_id, unit_cost |
| 353 | | (δ 1 (τ po_id DESC (purchase_order_item))) |
| 354 | | |
| 355 | | Temp1 ← warehouse ⨝ warehouse_id=warehouse_id warehouse_stock |
| 356 | | |
| 357 | | Temp2 ← Temp1 ⨝ product_id=product_id LatestCost |
| 358 | | |
| 359 | | Result ← γ warehouse.name → warehouse_location; |
| 360 | | SUM(quantity_on_hand × unit_cost) → total_inventory_valuation_mkd |
| 361 | | (Temp2) |
| 362 | | }}} |
| 363 | | |
| 364 | | ---- |
| 365 | | |
| 366 | | '''Символи и операции:''' |
| 367 | | * π - проекција (SELECT) |
| 368 | | * σ - селекција (WHERE) |
| 369 | | * ⨝ - природен спој (JOIN) |
| 370 | | * ⟕ - left outer join (LEFT JOIN) |
| 371 | | * × - Декартов производ (CROSS JOIN) |
| 372 | | * γ - групирање (GROUP BY) |
| 373 | | * τ - сортирање (ORDER BY) |
| 374 | | * δ - лимит (LIMIT) |
| 375 | | * ρ - преименување (AS / alias) |
| 376 | | * ∧ - логичко И (AND) |
| 377 | | * ∨ - логичко ИЛИ (OR) |
| | 4 | == 1. == |