21 β Analitik dizayn va ma'lumotlar ombori¶
β¬ οΈ Oldingi: 20 β NoSQL ma'lumot modellashtirish Β· π README Β· Keyingi: 22 β Partitioning, sharding va masshtablash β‘οΈ
Bu bobda: shu paytgacha biz tranzaksion (OLTP) sxemalar loyihaladik β normallashtirilgan, anomaliyasiz, ko'p kichik yozuv uchun. Endi teskari dunyoga kiramiz: analitik (OLAP) dizayn, ya'ni "choraklik tushumni viloyat bo'yicha ko'rsat" kabi savollarga millionlab qatordan tez javob beradigan ombor. Nega OLTP va OLAP alohida sxema talab qiladi; Kimball ning dimensional modeling metodologiyasi (fakt va o'lcham jadvallari); star va snowflake sxemalar trade-off'i; eng muhim qaror β grain (donalik); Slowly Changing Dimensions (SCD Type 1/2/3) bilan o'lcham tarixini saqlash; ETL vs ELT; ombor (warehouse) vs ko'l (lake); va nega bu yerda denormalizatsiya β norma. Hamma SQL PostgreSQL 18.4 da haqiqatan ishga tushirilgan.
0. Bu bob qayerda turadi¶
Butun kitob davomida bitta tamoyilni o'rgandik: normallashtir, takrorni yo'qot, anomaliyani oldini ol (7-8-boblar). Bu OLTP β Online Transaction Processing β uchun to'g'ri. Do'kon ilovasi buyurtma qabul qiladi, balansni yangilaydi, ombordan kamaytiradi: ko'p kichik, tez, izchil yozuvlar.
Ammo bir kuni rahbar so'raydi: "O'tgan chorakda qaysi kategoriya, qaysi viloyatda, qaysi kanalda eng ko'p sotildi?" Bu savolga javob berish uchun siz millionlab buyurtma qatorini o'qib, guruhlab, jamlashingiz kerak. Bu β OLAP β Online Analytical Processing. Va u butunlay boshqacha dizayn talab qiladi.
Bu bob shu ikkinchi dunyoning dizaynini o'rgatadi. Biz hali ham PostgreSQL ishlatamiz (alohida analitik baza yoki ombor sifatida), lekin qoidalar teskari: normalizatsiya emas β denormalizatsiya; ko'p tor indeks emas β kam, keng skanlash; "anomaliyani oldini ol" emas β "o'qishni tezlashtir".
Eslatma: SQL kitobining 11-bobi
GROUP BY/ agregat sintaksisini o'rgatgan. Buni qayta o'rgatmaymiz. Bu bob β qanday sxema shu agregat so'rovlarni millionlab qatorlarda tez qiladi.
1. OLTP vs OLAP β nega ikki xil dunyo¶
Tasavvur qiling, do'kon kassasi (OLTP) va buxgalteriya bo'limi (OLAP) bir xil daftarni ishlatadi. Kassir har soniyada yozuv qo'shadi; buxgalter esa oyiga bir marta butun daftarni varaqlab jamlaydi. Agar buxgalter daftarni soatlab varaqlasa β kassir yozolmay qoladi. Yechim: buxgalterga alohida, jamlashga qulay nusxa berish.
| Mezon | OLTP (tranzaksion) | OLAP (analitik) |
|---|---|---|
| Maqsad | Kundalik biznes amallari | Tahlil, hisobot, qaror |
| Tipik so'rov | "Buyurtma #501 ni o'qi/yangila" | "Yil bo'yicha kategoriya tushumi" |
| Yuk turi | Ko'p kichik INSERT/UPDATE |
Kam, lekin og'ir SELECT + agregat |
| Tegadigan qatorlar | Bir nechta | Millionlab |
| Sxema | Normallashtirilgan (3NF) | Denormallashtirilgan (star) |
| Indeks | Ko'p tor B-tree | Kam; ko'pincha to'liq skan |
| Saqlash | Qator bo'yicha (row-store) | Ustun bo'yicha (columnar) |
| Yangilanish | Doimiy, real vaqt | Davriy (kunlik/soatlik yuklash) |
| Mezon | Tranzaksiya tezligi (TPS), ACID | So'rov vaqti, tarix hajmi |
Nega bitta sxema ikkalasiga yaramaydi? Uch sabab:
- Resurs raqobati. Og'ir analitik so'rov (millionlab qatorni skanlash) tranzaksion bazaning xotira va diskini band qiladi -> mijozlar uchun ilova sekinlashadi.
- Sxema shakli. OLTP normallashtirilgan: bitta tushum savoli 8-10 ta
JOINtalab qiladi -> sekin. OLAP denormallashtirilgan: 1-2JOIN-> tez. - Tarix. OLTP odatda joriy holatni saqlaydi ("mijozning hozirgi shahri"). OLAP esa o'tmishni ("o'sha sotuv paytida qaysi shahar edi") β bu butunlay boshqa modellashtirish (SCD, 5-bo'lim).
Asosiy qaror: ma'lumotni tranzaksion bazadan analitik omborga ko'chirib, alohida loyihalangan sxemada saqlaymiz. Ko'chirish jarayoni β ETL/ELT (7-bo'lim).
2. Dimensional modeling: Kimball metodologiyasi¶
OLAP sxemasini loyihalashning eng mashhur usuli β dimensional modeling, uni Ralph Kimball mashhur qilgan. G'oya juda sodda va biznesga tushunarli:
Har bir biznes hodisasini ikki qismga ajrating: nima o'lchandi (raqamlar) va qanday kontekstda (matnli tavsiflar).
- Fakt jadvali (fact table) β o'lchanadigan hodisalarni saqlaydi: sotuv summasi, miqdor, voqea soni. Bu β raqamlar (measures). Juda ko'p qator, tor (kam ustun).
- O'lcham jadvali (dimension table) β kontekstni saqlaydi: qachon (vaqt), nima (mahsulot), kim (mijoz), qayerda (do'kon). Bu β matnli, tavsifiy atributlar. Kam qator, keng (ko'p ustun).
Hayotiy o'xshatish: chek qog'ozi. Fakt β "3 dona x 450 000 = 1 350 000". O'lchamlar β "Quloqchin Air" (mahsulot), "2026-04-12" (vaqt), "Olim" (mijoz), "Internet-magazin" (do'kon). Fakt β "qancha", o'lchamlar β "nima/qachon/kim/qayerda".
Analitik savol har doim shu shaklda: "[o'lchov]ni [o'lcham] bo'yicha [agregat] qil" β masalan "tushumni kategoriya bo'yicha SUM qil". Aynan shuning uchun bu model BI vositalariga (Power BI, Metabase, Tableau) juda mos.
2.1 Surrogate kalit β bu yerda majburiy¶
OLTP da surrogate vs natural kalit β trade-off edi (6-bob). Omborda esa o'lcham jadvallari deyarli doim kichik, ma'nosiz surrogate kalit (mahsulot_key, vaqt_key) ishlatadi. Sabab: fakt jadvali tabiiy kalitga (uzun SKU matni) emas, kichik int ga bog'lansa β joy tejaladi va JOIN tezroq. Bundan tashqari, SCD Type 2 (5-bo'lim) tabiiy kalit barqaror qolib, surrogate kalit har versiyada o'zgarishini talab qiladi.
3. Star schema vs Snowflake schema¶
Endi fakt va o'lchamlarni qanday bog'lash β ikki asosiy naqsh bor.
Star (yulduz) schema β markazda fakt jadvali, atrofida bevosita unga bog'langan denormallashgan o'lchamlar. Diagrammada yulduzga o'xshaydi. O'lcham jadvali ataylab takror saqlaydi: masalan dim_mahsulot da har qatorda kategoriya va brend matni to'g'ridan-to'g'ri yoziladi (alohida jadvalga ajratilmaydi).
Snowflake (qor uchqun) schema β o'lchamlar ham normallashtirilgan: dim_mahsulot dan kategoriya va brend alohida kichik jadvallarga ajratiladi, fakt -> o'lcham -> kichik-o'lcham zanjiri hosil bo'ladi. Diagrammada qor uchquniga o'xshaydi.
| Mezon | Star | Snowflake |
|---|---|---|
| O'lchamlar | Denormallashgan (takror bor) | Normallashgan (takror kam) |
JOIN soni |
Kam (1 fakt + 1 o'lcham) | Ko'p (zanjirli) |
| So'rov tezligi | Tezroq | Sekinroq |
| Disk joyi | Ko'proq (takror) | Kamroq |
| Murakkablik | Sodda | Murakkabroq |
| BI vositaga moslik | A'lo | O'rtacha |
Amaliyot qaroriga: ko'pchilik analitik ombor star ni tanlaydi. Omborda disk joyi arzon, lekin so'rov tezligi va soddalik qimmat. Snowflake faqat o'lcham juda katta bo'lib, takror jiddiy joy yeganda yoki o'lcham tez-tez yangilanganda mantiqiy. Denormalizatsiya bu yerda xato emas β norma: biz ataylab takror saqlaymiz, chunki maqsad o'qish tezligi.
4. Grain β eng muhim qaror¶
Fakt jadvalini loyihalashda birinchi va eng muhim qaror β grain (donalik): bitta fakt qatori aniq nimani ifodalaydi?
Grain ni bir jumlada, biznes tilida ayta olishingiz kerak. Misollar:
- "Bitta chekdagi bitta mahsulot qatori" (eng nozik β line item).
- "Bitta chek (jami)" (qatorlar yig'ilgan).
- "Bir kunlik, bir do'kondagi, bir mahsulot bo'yicha jami sotuv" (kunlik snapshot).
Grain ni birinchi tanlang, keyin o'lchamlar va o'lchovlarni unga moslang. Qoidalar:
- Eng nozik (atomic) grain ni afzal ko'ring β "bitta chek qatori". Nozik grain'dan har qanday yuqori darajani (chek, kun, oy) jamlash mumkin; teskarisi β imkonsiz. Agar kunlik jamlab saqlasangiz, keyin "soat bo'yicha" so'rashning iloji yo'q.
- Bitta fakt jadvalida grain aralashtirmang. "Chek qatori" va "kunlik jami" ni bitta jadvalga qo' shsangiz β
SUMikki marta sanaydi (double counting). Har grain β alohida fakt jadvali. - Har o'lchov (measure) grain ga mos bo'lsin. "Chek qatori" grain'ida
miqdor,narxmantiqiy. "Chekdagi yetkazib berish narxi" esa qator emas, butun chek darajasida β uni har qatorga takrorlash double counting beradi.
Ekspert maslahati: grain ni noto'g'ri tanlash β omborning eng qimmat xatosi, chunki uni keyin tuzatish butun yuklashni qayta qurish demakdir. Avval grain, keyin qolgani.
Quyida grain = "bitta sotuv chekidagi bitta mahsulot qatori" deb tanlangan star sxemani PostgreSQL da quramiz.
5. Amaliy: PostgreSQL da star schema (RUN)¶
Endi haqiqiy star sxema quramiz: 1 fakt + 4 o'lcham jadval, ma'lumot kiritamiz va agregat so'rovlar bilan jamlaymiz. Hammasi PG 18.4 da ishga tushirildi (izolyatsiya uchun ch21 schema).
CREATE SCHEMA IF NOT EXISTS ch21;
SET search_path = ch21;
-- O'lcham: vaqt (har omborning markaziy o'lchami)
CREATE TABLE dim_vaqt (
vaqt_key int PRIMARY KEY, -- 20260110 ko'rinishidagi "ma'noli" surrogate
sana date NOT NULL,
yil int NOT NULL,
chorak int NOT NULL,
oy int NOT NULL,
oy_nomi text NOT NULL,
hafta_kuni text NOT NULL,
dam_kuni boolean NOT NULL
);
-- O'lcham: mahsulot (denormallashgan β kategoriya/brend SHU YERDA)
CREATE TABLE dim_mahsulot (
mahsulot_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
sku text NOT NULL,
nom text NOT NULL,
kategoriya text NOT NULL,
brend text NOT NULL
);
-- O'lcham: mijoz
CREATE TABLE dim_mijoz (
mijoz_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ism text NOT NULL,
shahar text NOT NULL,
segment text NOT NULL -- 'B2C' / 'B2B'
);
-- O'lcham: do'kon (joy + kanal)
CREATE TABLE dim_dokon (
dokon_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom text NOT NULL,
viloyat text NOT NULL,
kanal text NOT NULL -- 'online' / 'offline'
);
-- FAKT: sotuv. GRAIN = "bitta chekdagi bitta mahsulot qatori".
CREATE TABLE fakt_sotuv (
sotuv_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
vaqt_key int NOT NULL REFERENCES dim_vaqt(vaqt_key),
mahsulot_key int NOT NULL REFERENCES dim_mahsulot(mahsulot_key),
mijoz_key int NOT NULL REFERENCES dim_mijoz(mijoz_key),
dokon_key int NOT NULL REFERENCES dim_dokon(dokon_key),
-- o'lchovlar (measures) β jamlanadigan raqamlar:
miqdor int NOT NULL,
narx numeric(12,2) NOT NULL,
jami numeric(14,2) GENERATED ALWAYS AS (miqdor * narx) STORED
);
E'tibor bering: fakt jadvali tor (faqat 4 ta o'lcham kaliti + 3 ta o'lchov) lekin millionlab qatorga cho'ziladi. O'lchamlar keng lekin kam qatorli. Bu β klassik star shakli. jami ustuni GENERATED ... STORED (10-bobdan) β har sotuv qatori uchun avtomatik hisoblanadi.
Ma'lumotni to'ldiramiz:
SET search_path = ch21;
INSERT INTO dim_vaqt (vaqt_key, sana, yil, chorak, oy, oy_nomi, hafta_kuni, dam_kuni) VALUES
(20260110,'2026-01-10',2026,1,1,'Yanvar','Shanba',true),
(20260115,'2026-01-15',2026,1,1,'Yanvar','Payshanba',false),
(20260205,'2026-02-05',2026,1,2,'Fevral','Payshanba',false),
(20260320,'2026-03-20',2026,1,3,'Mart','Juma',false),
(20260412,'2026-04-12',2026,2,4,'Aprel','Yakshanba',true);
INSERT INTO dim_mahsulot (sku, nom, kategoriya, brend) VALUES
('LP-100','Notebook Pro 14','Noutbuk','Acme'),
('PH-200','Smartfon X','Telefon','Acme'),
('HD-300','Quloqchin Air','Aksessuar','Sona');
INSERT INTO dim_mijoz (ism, shahar, segment) VALUES
('Olim','Toshkent','B2C'),
('Dilnoza','Samarqand','B2C'),
('TexnoSavdo MChJ','Toshkent','B2B');
INSERT INTO dim_dokon (nom, viloyat, kanal) VALUES
('Markaziy do''kon','Toshkent','offline'),
('Internet-magazin','Toshkent','online');
INSERT INTO fakt_sotuv (vaqt_key, mahsulot_key, mijoz_key, dokon_key, miqdor, narx) VALUES
(20260110, 1, 1, 1, 1, 12000000.00),
(20260110, 3, 1, 1, 2, 450000.00),
(20260115, 2, 2, 2, 1, 6500000.00),
(20260205, 1, 3, 2, 5, 11500000.00),
(20260205, 3, 3, 2,10, 420000.00),
(20260320, 2, 1, 1, 2, 6700000.00),
(20260412, 3, 2, 2, 3, 450000.00);
5.1 Agregat so'rovlar (star join + GROUP BY)¶
Endi analitik savollarga javob beramiz. Har biri β fakt + o'lcham JOIN + GROUP BY o'lcham atributi bo'yicha.
Savol 1: qaysi kategoriya eng ko'p tushum berdi?
SET search_path = ch21;
SELECT m.kategoriya, SUM(f.jami) AS tushum, SUM(f.miqdor) AS sotilgan_dona
FROM fakt_sotuv f
JOIN dim_mahsulot m ON m.mahsulot_key = f.mahsulot_key
GROUP BY m.kategoriya
ORDER BY tushum DESC;
PG 18.4 da haqiqiy natija:
kategoriya | tushum | sotilgan_dona
------------+-------------+---------------
Noutbuk | 69500000.00 | 6
Telefon | 19900000.00 | 3
Aksessuar | 6450000.00 | 15
(3 rows)
Savol 2: chorak + kanal bo'yicha tushum (ikki o'lchamli kesim).
SET search_path = ch21;
SELECT v.chorak, d.kanal, SUM(f.jami) AS tushum
FROM fakt_sotuv f
JOIN dim_vaqt v ON v.vaqt_key = f.vaqt_key
JOIN dim_dokon d ON d.dokon_key = f.dokon_key
GROUP BY v.chorak, d.kanal
ORDER BY v.chorak, d.kanal;
chorak | kanal | tushum
--------+---------+-------------
1 | offline | 26300000.00
1 | online | 68200000.00
2 | online | 1350000.00
(3 rows)
Savol 3: mijoz segmenti bo'yicha o'rtacha qator qiymati.
SET search_path = ch21;
SELECT mz.segment, COUNT(*) AS qatorlar, ROUND(AVG(f.jami),0) AS ortacha_qator
FROM fakt_sotuv f
JOIN dim_mijoz mz ON mz.mijoz_key = f.mijoz_key
GROUP BY mz.segment
ORDER BY mz.segment;
segment | qatorlar | ortacha_qator
---------+----------+---------------
B2B | 2 | 30850000
B2C | 5 | 6830000
(2 rows)
E'tibor bering β uchala so'rov ham bir xil shaklda: fakt'dan SUM/AVG, o'lchamdan GROUP BY. Bu β star sxemaning kuchi: har qanday biznes savoli shu naqshga tushadi.
ROLLUP/CUBE: PostgreSQLGROUP BY ROLLUP (yil, oy)yokiCUBE (...)bilan bir so'rovda ham detal, ham oraliq jamlamalarni beradi β analitik hisobotlar uchun juda qulay.GROUP BYhaqida SQL kitobining 11-bobida o'qishingiz mumkin.
6. Slowly Changing Dimensions (SCD)¶
Eng nozik savol: o'lcham vaqt o'tishi bilan o'zgarsa nima qilamiz? Olim Toshkentdan Samarqandga ko'chdi. Uning eski sotuvlari qaysi shaharga tegishli β Toshkentgami (o'sha paytdagi haqiqat) yoki Samarqandgami (hozirgi haqiqat)?
Bu β Slowly Changing Dimension muammosi. "Slowly" β chunki o'lcham atributlari kamdan-kam (mijoz ko'chishi, mahsulot kategoriyasi o'zgarishi), lekin o'zgaradi. Kimball uch asosiy strategiyani nomlaydi.
6.1 Type 1 β ustiga yozish¶
Eski qiymat o'chiriladi, faqat oxirgi holat qoladi:
Tarix yo'q. Endi Olimning barcha eski sotuvlari ham Samarqandga "ko'chadi" -> "Toshkentda o'tgan yilgi tushum" xato chiqadi. Type 1 ni faqat xato tuzatish uchun ishlating ("shaxar nomi xato yozilgan edi"), real o'zgarish uchun emas.
6.2 Type 2 β yangi qator + amal davri / joriy bayroq¶
Eng keng tarqalgan strategiya: eski qatorni yopamiz (amal oxirini belgilaymiz, joriy=false), yangi qator qo'shamiz. Bu β 18-bobdagi temporal/tarix naqshining ombordagi ko'rinishi. PG 18.4 da quramiz:
SET search_path = ch21;
CREATE TABLE dim_mijoz_scd2 (
mijoz_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- surrogate (har versiya yangi)
mijoz_id int NOT NULL, -- tabiiy (business) kalit β barqaror
ism text NOT NULL,
shahar text NOT NULL,
amal_boshi date NOT NULL,
amal_oxiri date, -- NULL = hali amalda
joriy boolean NOT NULL DEFAULT true,
UNIQUE (mijoz_id, amal_boshi)
);
-- Boshlang'ich: Olim Toshkentda
INSERT INTO dim_mijoz_scd2 (mijoz_id, ism, shahar, amal_boshi, amal_oxiri, joriy)
VALUES (1001, 'Olim', 'Toshkent', '2026-01-01', NULL, true);
-- 2026-03-01 da ko'chdi: eski qatorni YOP, yangi qator QO'SH
UPDATE dim_mijoz_scd2
SET amal_oxiri = '2026-02-28', joriy = false
WHERE mijoz_id = 1001 AND joriy;
INSERT INTO dim_mijoz_scd2 (mijoz_id, ism, shahar, amal_boshi, amal_oxiri, joriy)
VALUES (1001, 'Olim', 'Samarqand', '2026-03-01', NULL, true);
Endi mijoz 1001 uchun ikki qator bor β biri yopilgan, biri joriy:
SET search_path = ch21;
SELECT mijoz_key, mijoz_id, shahar, amal_boshi, amal_oxiri, joriy
FROM dim_mijoz_scd2 WHERE mijoz_id = 1001 ORDER BY amal_boshi;
PG 18.4 da haqiqiy natija:
mijoz_key | mijoz_id | shahar | amal_boshi | amal_oxiri | joriy
-----------+----------+-----------+------------+------------+-------
1 | 1001 | Toshkent | 2026-01-01 | 2026-02-28 | f
2 | 1001 | Samarqand | 2026-03-01 | | t
(2 rows)
Sehr shu yerda: fakt jadval mijoz_key (surrogate) ga bog'lanadi. 2026-02 dagi sotuv mijoz_key=1 (Toshkent) ga, 2026-04 dagi sotuv mijoz_key=2 (Samarqand) ga ulanadi. Shunday qilib har sotuv o'sha paytdagi holatni saqlaydi β bu Type 2 ning butun maqsadi.
Joriy holat va "o'sha paytda qaysi shahar edi" so'rovlari:
SET search_path = ch21;
-- Joriy holat (faqat aktiv qator)
SELECT mijoz_id, ism, shahar FROM dim_mijoz_scd2 WHERE mijoz_id = 1001 AND joriy;
-- "2026-01-15 da Olim qaysi shaharda edi?" (point-in-time, 18-bob mantig'i)
SELECT shahar FROM dim_mijoz_scd2
WHERE mijoz_id = 1001
AND DATE '2026-01-15' >= amal_boshi
AND (amal_oxiri IS NULL OR DATE '2026-01-15' <= amal_oxiri);
mijoz_id | ism | shahar
----------+------+-----------
1001 | Olim | Samarqand
(1 row)
shahar
----------
Toshkent
(1 row)
Joriy so'rov "Samarqand" (hozir), point-in-time so'rov "Toshkent" (2026-01-15 da) qaytardi β aynan kutilgani. Tozalaymiz:
6.3 Type 3 β oldingi qiymat ustuni¶
O'rta yo'l: qo'shimcha ustunda faqat bitta oldingi qiymat saqlanadi:
-- joriy_shahar = 'Samarqand', oldingi_shahar = 'Toshkent'
ALTER TABLE dim_mijoz ADD COLUMN oldingi_shahar text;
UPDATE dim_mijoz SET oldingi_shahar = shahar, shahar = 'Samarqand' WHERE mijoz_key = 1;
Faqat "avval/keyin" taqqoslash kerak bo'lganda foydali (masalan, "viloyatni o'zgartirgan mijozlar tahlili"). To'liq tarix saqlanmaydi β undan oldingi qiymat yo'qoladi.
| Type | Tarix | Qachon ishlatish |
|---|---|---|
| Type 1 | Yo'q (ustiga yozish) | Xato tuzatish; tarix muhim emas |
| Type 2 | To'liq (har versiya qator) | "O'sha paytdagi holat" muhim (eng keng) |
| Type 3 | Cheklangan (1 oldingi) | "Avval/keyin" taqqoslash, to'liq tarix shart emas |
Bog'lanish: Type 2 β bu aslida 18-bobdagi valid-time temporal naqshning analitik nomi. SCD β temporal modellashtirishning ombor jargoni.
7. ETL vs ELT β ma'lumotni omborga qanday ko'chiramiz¶
OLTP bazadan analitik omborga ma'lumot ko'chiriladi (real vaqtda emas, davriy). Ikki yondashuv bor:
- ETL (Extract -> Transform -> Load): ma'lumotni olib, tashqarida (alohida vositada) o'zgartirib/tozalab, keyin omborga yuklash. An'anaviy; ombor cheklangan quvvatga ega bo'lganda mantiqiy edi.
- ELT (Extract -> Load -> Transform): ma'lumotni olib, omborga xom holda yuklash, keyin ombor ichida SQL bilan o'zgartirish. Zamonaviy bulutli omborlar (BigQuery, Snowflake) kuchli bo'lgani uchun bu ustun bo'ldi β "transform" ni baza o'zi qiladi.
PostgreSQL kontekstida oddiy ELT β tranzaksion jadvallardan o'qib, fakt/o'lcham jadvallariga INSERT ... SELECT qilish (ko'pincha tunda, cron bilan), yoki materialized view (15-bobdan) ni davriy REFRESH qilish.
-- Illustrativ ELT: tranzaksion buyurtmalardan kunlik faktni qurish
-- (har tun cron bilan: REFRESH MATERIALIZED VIEW kunlik_sotuv;)
CREATE MATERIALIZED VIEW kunlik_sotuv AS
SELECT date_trunc('day', yaratilgan) AS kun,
mahsulot_id,
SUM(miqdor) AS jami_miqdor,
SUM(miqdor*narx) AS jami_summa
FROM oltp_buyurtma_qatori
GROUP BY 1, 2;
Halollik eslatmasi: yuqoridagi blok illustrativ (manba
oltp_buyurtma_qatorijadvali bu schemada mavjud emas), shuning uchun u RUN qilinmadi. 5-bo'limdagi star sxema esa to'liq RUN qilingan.
8. Ombor vs ko'l vs columnar β qisqacha¶
Ma'lumotlar ombori (data warehouse) β bu bob mavzusi: strukturalangan, sxemasi oldindan loyihalangan (schema-on-write), tozalangan, analitikaga tayyor ma'lumot. Star/snowflake bu yerda yashaydi.
Ma'lumotlar ko'li (data lake) β xom, har xil formatdagi (JSON, log, rasm, parquet) ma'lumotni "o'qishda izohlash" (schema-on-read) bilan saqlaydi. Avval to'plab, keyin "nima qilish" ni hal qilasiz. Arzon, moslashuvchan, lekin tartibsiz bo'lib ketishi mumkin ("data swamp" β botqoq). Ko'pincha ikkalasi birga ishlatiladi: ko'lga xom to'plab, omborga tozalab yuklash ("lakehouse").
Columnar (ustunli) saqlash β nega OLAP uchun muhim:
- Row-store (OLTP, PG ning standarti): bir qator barcha ustunlari yonma-yon diskda. "Bitta buyurtmani o'qish" tez.
- Columnar (OLAP): bir ustun barcha qiymatlari yonma-yon.
SUM(jami)faqatjamiustunini o'qiydi, qolganini emas -> millionlab qatorni jamlash tez. Bir xil qiymatlar yonma-yon bo'lgani uchun siqish (compression) ham yaxshi.
PostgreSQL standartan row-store, lekin analitik yuk uchun kengaytmalar mavjud (masalan cstore/ustunli FDW lar yoki tashqi analitik enginelar). Maxsus omborlar (ClickHouse, BigQuery, Snowflake) tug'ma columnar.
9. Bu bobning xulosasi (dizayn yodnomasi)¶
- OLTP va OLAP β ikki xil maqsad, ikki xil sxema. Analitik yukni tranzaksion bazada ishlatmang.
- Dimensional modeling: fakt (o'lchanadigan raqamlar) + o'lcham (kontekst). Surrogate kalit majburiy.
- Star ni standart deb biling, snowflake ni faqat zarurat bo'lganda. Denormalizatsiya β bu yerda norma.
- Grain ni birinchi va aniq tanlang; eng nozik darajani afzal ko'ring; grain aralashtirmang.
- SCD Type 2 β o'lcham tarixini saqlashning standart usuli (18-bob temporal naqshi).
- ELT zamonaviy norma; ombor ichida transform. Columnar saqlash OLAP ni tezlashtiradi.
Mashqlar¶
Oson¶
- OLTP yoki OLAP? Quyidagi har bir ish yukini OLTP yoki OLAP deb belgilang va bir jumlada asoslang: (a) bankomat balansni yangilaydi; (b) marketing bo'limi "oxirgi 3 yil oylik tushum trendi" ni so'raydi; (c) foydalanuvchi parolini o'zgartiradi; (d) BI dashboard har viloyat bo'yicha sotuvni ko'rsatadi.
- Fakt yoki o'lcham? Onlayn-do'kon ombori uchun quyidagilarni fakt jadvaliga yoki o'lcham jadvaliga ajrating: sotuv summasi, mahsulot nomi, mijoz shahri, sotilgan miqdor, sana, to'lov turi, chegirma foizi.
- Grain ni bir jumlada ayting β quyidagi fakt jadvali uchun:
fakt_qoldiq(kun_key, ombor_key, mahsulot_key, qoldiq_dona). Bu jadvaldaSUM(qoldiq_dona)ni bir kun bo'yicha hisoblash mantiqiymi? - Star schema chizing β taksi xizmati uchun: fakt = "bitta safar". Qaysi o'lchamlar (kamida 3 ta) va qaysi o'lchovlar (kamida 2 ta) bo'lishini sanab bering.
O'rta¶
- SCD turini tanlang. Har holat uchun SCD Type 1/2/3 dan birini tanlang va asoslang: (a) mahsulot nomida imlo xatosi tuzatilmoqda; (b) mijoz familiyasini o'zgartirdi va eski hisobotlar eski familiyani ko'rsatishi kerak; (c) "sotuvchining hozirgi va bir oldingi mintaqasi" ni taqqoslash kerak.
- Snowflake'ni star'ga aylantiring. Sizda snowflake bor:
fakt_sotuv -> dim_mahsulot(kategoriya_id) -> dim_kategoriya(nom, bolim_id) -> dim_bolim(nom). Buni star ga aylantiring (dim_mahsulotni qanday o'zgartirasiz) va bitta afzallik/bitta kamchilikni yozing. - Grain xatosini toping. Bir jamoa
fakt_sotuv(chek_key, mahsulot_key, miqdor, chek_jami_summa, yetkazib_berish_narxi)quribdi.SUM(yetkazib_berish_narxi)ni mahsulot kategoriyasi bo'yicha hisoblamoqchi. Bu yerda qanday double counting xatosi bor va qanday tuzatasiz? - Vaqt o'lchamini loyihalang.
dim_vaqtjadvali uchun kamida 8 ta foydali ustun sanang (kun darajasidagi grain uchun). Nega omborWHERE sana BETWEEN ...o'rnigaJOIN dim_vaqt ... WHERE dim_vaqt.chorak = 1ni afzal ko'radi?
Qiyin¶
- OLTP -> OLAP o'tkazish. Normallashtirilgan OLTP sxemasi berilgan:
buyurtma(id, mijoz_id, sana),buyurtma_qatori(buyurtma_id, mahsulot_id, miqdor, narx),mahsulot(id, nom, kategoriya_id),kategoriya(id, nom),mijoz(id, ism, shahar_id),shahar(id, nom, viloyat). Bundan "bitta buyurtma qatori" grain' idagi star analitik sxemani loyihalang: fakt jadval DDL'i, kamida 3 o'lcham jadval DDL'i, va OLTP'dan faktga to'ldiruvchi bittaINSERT ... SELECT(illustrativ) yozing. - SCD Type 2 to'liq.
dim_mahsulotuchun SCD Type 2 ni loyihalang: narx kategoriyasi ("arzon"/"premium") vaqt bilan o'zgaradi. Jadval DDL (surrogate + business key + amal_boshi/amal_oxiri/joriy), o'zgarishni amalga oshiruvchiUPDATE+INSERT, va "2026-yil 1-iyulda mahsulot X qaysi kategoriyada edi" so'rovini yozing. - Double counting tahlili.
fakt_buyurtma(buyurtma_key, mijoz_key, jami_summa)da grain = "bitta buyurtma". Tahlilchi bu faktnidim_buyurtma_qatori(har qatorda mahsulot) bilanJOINqilibSUM(jami_summa)ni mahsulot bo'yicha hisobladi va g'alati katta raqam oldi. Nega? Buni to'g'ri qilish uchun qanday grain'dagi fakt kerak? - Ombor vs ko'l qarori. Bir startap log fayllar (JSON, kuniga 50 GB), struktura tez-tez o'zgaradi, ma'lumotlar olimlari turli tahlil qilmoqchi β lekin moliya bo'limi aniq, barqaror "oylik tushum" hisobotini ham xohlaydi. Qaysi ma'lumotni ko'lga, qaysisini omborga joylashtirasiz va nega? Star sxema qaysi qismda yashaydi?
Yechimlar¶
Yechim β 1
(a) OLTP β bitta yozuvni tez yangilash, ACID muhim. (b) OLAP β millionlab tarixiy qatorni jamlash. (c) OLTP β bitta qatorni yangilash. (d) OLAP β agregat + guruhlash.
Qoida: bitta yozuvga tegadigan, real-vaqt, izchillik talab qiluvchi -> OLTP. Ko'p qatorni o'qib jamlaydigan, tarixiy, hisobot -> OLAP.
Yechim β 2
Fakt (o'lchanadigan raqamlar, jamlanadi): sotuv summasi, sotilgan miqdor, chegirma foizi.
O'lcham (kontekst, tavsifiy): mahsulot nomi (dim_mahsulot), mijoz shahri (dim_mijoz), sana (dim_vaqt), to'lov turi (dim_tolov yoki kichik o'lcham).
Chegirma foizi chegaraviy: agar har sotuv qatori uchun bo'lsa β o'lchov (fakt'da); agar mahsulotning doimiy xususiyati bo'lsa β o'lcham atributi. Grain ga bog'liq.
Yechim β 3
Grain: "bir kun oxiridagi, bitta ombordagi, bitta mahsulotning qoldiq miqdori" (snapshot fakt).
SUM(qoldiq_dona) ni bir kun bo'yicha hisoblash mantiqiy emas, agar siz kunlar bo'ylab jamlasangiz β bu double counting (qoldiq yig'iladigan emas, balki holat o'lchovi). Bir kun ichida ombor/mahsulot bo'yicha jamlash mumkin, lekin kunlar bo'ylab SUM o'rniga oxirgi kun qiymatini olish yoki AVG kerak. Bu β "semi-additive" o'lchov (faqat ba'zi o'lchamlar bo'yicha qo'shiladi).
Yechim β 4
Grain = "bitta taksi safari".
O'lchamlar: dim_vaqt (safar boshlangan sana/soat), dim_haydovchi, dim_yo'lovchi, dim_hudud (boshlanish/tugash mintaqasi), dim_tolov_turi.
O'lchovlar (fakt'da): masofa (km), narx (so'm), davomiylik (daqiqa), kutish vaqti.
DDL eskizi:
Yechim β 5
(a) Type 1 β bu xato tuzatish, tarix kerak emas, ustiga yozish.
(b) Type 2 β eski hisobotlar eski familiyani ko'rsatishi kerak -> yangi qator + amal davri, har sotuv o'sha paytdagi key ga bog'lanadi.
(c) Type 3 β faqat "hozirgi va bir oldingi" taqqoslash kerak -> joriy_mintaqa + oldingi_mintaqa ustunlari.
Yechim β 6
Star ga aylantirish: dim_mahsulot ga kategoriya va bolim matnini to'g'ridan-to'g'ri ustun sifatida qo'shamiz (denormalizatsiya), dim_kategoriya va dim_bolim jadvallarini o'chiramiz:
CREATE TABLE dim_mahsulot (
mahsulot_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom text NOT NULL,
kategoriya text NOT NULL, -- avval alohida jadval edi
bolim text NOT NULL -- avval alohida jadval edi
);
Afzallik: kategoriya bo'yicha so'rov endi JOINsiz (yoki 1 JOIN) -> tezroq, BI vositaga qulay.
Kamchilik: takror β har mahsulot qatorida kategoriya/bolim matni qaytariladi -> ko'proq joy; agar bolim nomi o'zgarsa, ko'p qatorni yangilash kerak (lekin omborda o'zgarish kam).
Yechim β 7
Xato: chek_jami_summa va yetkazib_berish_narxi chek darajasidagi qiymatlar, lekin jadval grain'i "chek qatori" (har mahsulot uchun bitta qator). Bitta chek 3 mahsulotdan iborat bo'lsa, yetkazib_berish_narxi 3 marta takrorlanadi -> SUM uni 3 baravar sanaydi.
Tuzatish: chek darajasidagi o'lchovlarni alohida fakt jadvaliga chiqaring (grain = "bitta chek"):
CREATE TABLE fakt_chek ( -- grain: bitta chek
chek_key int PRIMARY KEY,
mijoz_key int, vaqt_key int,
chek_jami_summa numeric(14,2),
yetkazib_berish_narxi numeric(10,2)
);
CREATE TABLE fakt_chek_qatori ( -- grain: chekdagi mahsulot qatori
chek_key int, mahsulot_key int,
miqdor int, narx numeric(12,2)
);
Endi yetkazib_berish_narxi ni fakt_chek dan, mahsulot detalini fakt_chek_qatori dan jamlaysiz β double counting yo'q.
Yechim β 8
dim_vaqt ustunlari: vaqt_key, sana, yil, chorak, oy, oy_nomi, hafta_kuni, hafta_raqami, dam_kuni (boolean), bayram (boolean), moliya_yili.
Nega JOIN dim_vaqt afzal: vaqt o'lchami "biznes taqvimi" mantig'ini (chorak, moliya yili, bayram, dam kuni) bir joyda saqlaydi. WHERE sana BETWEEN har so'rovda shu mantiqni qayta yozishni talab qiladi va "moliya choragi" yoki "bayram kunlari" kabi murakkab tushunchalarni ifodalay olmaydi. dim_vaqt ularni tayyor ustun qiladi -> so'rov sodda va izchil.
Yechim β 9
Grain = "bitta buyurtma qatori".
-- O'lchamlar
CREATE TABLE dim_vaqt (
vaqt_key int PRIMARY KEY, sana date, yil int, chorak int, oy int);
CREATE TABLE dim_mahsulot (
mahsulot_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom text, kategoriya text); -- kategoriya denormallashgan
CREATE TABLE dim_mijoz (
mijoz_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ism text, shahar text, viloyat text); -- shahar+viloyat denormallashgan
-- Fakt
CREATE TABLE fakt_sotuv (
sotuv_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
vaqt_key int NOT NULL REFERENCES dim_vaqt(vaqt_key),
mahsulot_key int NOT NULL REFERENCES dim_mahsulot(mahsulot_key),
mijoz_key int NOT NULL REFERENCES dim_mijoz(mijoz_key),
miqdor int NOT NULL,
narx numeric(12,2) NOT NULL,
jami numeric(14,2) GENERATED ALWAYS AS (miqdor*narx) STORED);
-- ELT to'ldirish (illustrativ β o'lchamlar to'ldirilgan deb hisoblaymiz)
INSERT INTO fakt_sotuv (vaqt_key, mahsulot_key, mijoz_key, miqdor, narx)
SELECT to_char(b.sana,'YYYYMMDD')::int,
dm.mahsulot_key, dmz.mijoz_key,
bq.miqdor, bq.narx
FROM buyurtma_qatori bq
JOIN buyurtma b ON b.id = bq.buyurtma_id
JOIN mahsulot mh ON mh.id = bq.mahsulot_id
JOIN dim_mahsulot dm ON dm.nom = mh.nom
JOIN mijoz mj ON mj.id = b.mijoz_id
JOIN dim_mijoz dmz ON dmz.ism = mj.ism;
Asosiy g'oya: snowflake'simon OLTP zanjiri (kategoriya, shahar->viloyat) o'lcham jadvallariga yassilanadi (denormalizatsiya).
Yechim β 10
CREATE TABLE dim_mahsulot_scd2 (
mahsulot_key int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
mahsulot_id int NOT NULL, -- business key, barqaror
nom text NOT NULL,
narx_kat text NOT NULL, -- 'arzon' / 'premium'
amal_boshi date NOT NULL,
amal_oxiri date,
joriy boolean NOT NULL DEFAULT true,
UNIQUE (mahsulot_id, amal_boshi)
);
-- Boshlang'ich: X 'arzon'
INSERT INTO dim_mahsulot_scd2 (mahsulot_id, nom, narx_kat, amal_boshi, amal_oxiri, joriy)
VALUES (500, 'Mahsulot X', 'arzon', '2026-01-01', NULL, true);
-- 2026-06-01 da 'premium' ga o'tdi
UPDATE dim_mahsulot_scd2 SET amal_oxiri = '2026-05-31', joriy = false
WHERE mahsulot_id = 500 AND joriy;
INSERT INTO dim_mahsulot_scd2 (mahsulot_id, nom, narx_kat, amal_boshi, amal_oxiri, joriy)
VALUES (500, 'Mahsulot X', 'premium', '2026-06-01', NULL, true);
-- "2026-07-01 da X qaysi kategoriyada edi?"
SELECT narx_kat FROM dim_mahsulot_scd2
WHERE mahsulot_id = 500
AND DATE '2026-07-01' >= amal_boshi
AND (amal_oxiri IS NULL OR DATE '2026-07-01' <= amal_oxiri);
-- -> 'premium'
Yechim β 11
Nega katta raqam: grain "bitta buyurtma" bo'lgan faktni "buyurtma qatori" (har mahsulot) bilan JOIN qilish β har buyurtma uchun jami_summa ni shu buyurtmadagi qatorlar soniga ko'paytiradi. 3 mahsulotli buyurtma uchun jami_summa 3 marta sanaladi -> double counting.
To'g'ri yechim: mahsulot bo'yicha tahlil uchun qator grain'idagi fakt kerak: fakt_sotuv_qatori(buyurtma_key, mahsulot_key, qator_summa), bu yerda qator_summa = shu mahsulot qatori summasi. Keyin SUM(qator_summa) mahsulot bo'yicha to'g'ri ishlaydi. Buyurtma darajasidagi jami_summa (yetkazib berish, chegirma) esa alohida fakt_buyurtma da qoladi.
Yechim β 12
Ko'lga (data lake): xom JSON loglar (kuniga 50 GB), tez o'zgaruvchi struktura, ma'lumot olimlarining ad-hoc tahlili. Sabab: schema-on-read, arzon saqlash, moslashuvchanlik; oldindan sxema loyihalashning hojati yo'q.
Omborga (warehouse): moliyaning aniq, barqaror "oylik tushum" hisoboti. Sabab: bu strukturalangan, takrorlanadigan, ishonchli bo'lishi kerak -> dimensional model (fakt_tushum + dim_vaqt + dim_mahsulot).
Star sxema omborda yashaydi. Amaliy oqim: xom loglar ko'lga tushadi -> ELT bilan tozalanib/jamlanib omborning star sxemasiga yuklanadi (lakehouse naqshi). Ma'lumot olimlari ko'ldan erkin ishlaydi, moliya esa ishonchli ombordan hisobot oladi.
β¬ οΈ Oldingi: 20 β NoSQL ma'lumot modellashtirish Β· π README Β· Keyingi: 22 β Partitioning, sharding va masshtablash β‘οΈ