Mundarija (27)
- Bu darsda
- 1. Nega bu kerak?
- 2. Yassi jadval va uch anomaliya
- 2.1 Tajriba jadvali
- 2.2 Katakdagi ro'yxat
- 2.3 Update anomaliyasi
- 2.4 Delete anomaliyasi
- 2.5 Insert anomaliyasi
- 3. Funksional bog'liqlik
- 3.1 Ta'rif
- 3.2 So'rov bilan tekshirish
- 4. Normal formalar zinapoyasi
- 4.1 1NF — atomar qiymatlar
- 4.2 2NF — kalitning bir qismiga bog'liqlik yo'q
- 4.3 3NF — tranzitiv bog'liqlik yo'q
- 4.4 BCNF — har aniqlovchi kalit bo'lsin
- 5. 3NF ga ko'chirish
- 6. vazifalar sxemasi qaysi formada?
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson)
- 2-mashq (o'rta)
- 3-mashq (qiyin)
- 9. SQL qadami
- 10. Real ishda
- Xulosa
- Manbalar
Normalizatsiya: anomaliyalar, funksional bog'liqlik va 1NF–BCNF
Qisqacha: Normalizatsiya — har bir faktni bazada bir joyda saqlash qoidalari. Bir fakt bir necha qatorda takrorlansa, uch xil xato (anomaliya) paydo bo'ladi: yangilashda ziddiyat, o'chirishda ma'lumot yo'qolishi, qo'shishda imkonsizlik. 1NF, 2NF, 3NF va BCNF — takrorlanishni bosqichma-bosqich yo'qotadigan tekshiruvlar; ularning hammasi bitta savolga tayanadi: "qaysi ustun qaysi ustunni aniqlaydi?"
Bu darsda
- Update, insert va delete anomaliyalarini jonli misolda ko'rasiz.
- Funksional bog'liqlik nima ekanini tushuntirib, uni SQL so'rovi bilan tekshira olasiz.
- 1NF, 2NF, 3NF va BCNF buzilishini jadvaldan tanib olasiz.
- Yassi jadvalni
INSERT … SELECTbilan 3NF jadvallarga ko'chirasiz. vazifalarsxemasi qaysi normal formada ekanini va qayerda ongli istisno borligini bilasiz.
Oldin bilishingiz kerak: Bog'lanishlar: 1:1, 1:N, N:M, Agregatlar, GROUP BY va HAVING, UPDATE va DELETE xavfsiz.
1. Nega bu kerak?
Ko'p loyiha Excel jadvaldan boshlanadi. Malika opa kichik do'konining buyurtmalarini bitta varaqda yuritadi: har qatorda — buyurtma, mijozning ismi, telefoni, manzili, mahsulot. Qulay: hammasi ko'z oldida.
Bir oydan keyin muammolar boshlanadi. Mijoz manzilini o'zgartirdi — Malika opa uni oxirgi buyurtmada tuzatdi, oldingi o'ntasida eski manzil qoldi. Endi qaysi biri to'g'ri? Mijoz hamma buyurtmalarini bekor qildi — qatorlar o'chdi va u bilan birga mijozning telefoni ham yo'qoldi.
Bu tasodif emas. Bitta fakt ("Alining manzili") o'nta joyda yozilgan — o'n joyni bir xil saqlash insonning ham, dasturning ham qo'lidan kelmaydi. O'tgan darsda jadvallarni bog'lashni o'rgandik. Bu darsda — qaysi ustun qaysi jadvalga tushishini aniq qoidalar bilan hal qilamiz.
2. Yassi jadval va uch anomaliya
2.1 Tajriba jadvali
Kanon tajribasi vazifalar ning "Excel varianti"ni quradi: hamma narsa bitta jadvalda, teglar bitta katakda vergul bilan. Bu va keyingi so'rovlar — kanon sorovlar/35-normalizatsiya.sql (bo'sh tajriba bazasida, PostgreSQL 18.6). Telefon ekraniga sig'ishi uchun uzun qatorlarni bo'lib yozdik va o'z PostgreSQL 17.6 klasterimizda qayta ishga tushirdik — chiqish kanon bilan bir xil:
CREATE TABLE t35_yassi (
vazifa_id int PRIMARY KEY,
matn text,
egasi_email text,
egasi_ism text,
egasi_shahar text,
teglar text
);
INSERT INTO t35_yassi VALUES
(1, 'Bozordan kartoshka', 'aziz@example.com',
'Aziz Karimov', 'Toshkent', 'bozor,uy'),
(2, 'Jo''mrak almashtirish', 'aziz@example.com',
'Aziz Karimov', 'Toshkent', 'ta''mir,uy'),
(3, 'IELTS mashqi', 'dilnoza@example.com',
'Dilnoza Rahimova', 'Samarqand', 'ingliz'),
(4, 'Erta yotish', 'dilnoza@example.com',
'Dilnoza Rahimova', 'Samarqand', 'uyqu');CREATE TABLE
INSERT 0 4Aziz haqidagi uch fakt (email, ism, shahar) ikki marta yozildi. Dilnoza haqidagi ham. Endi har bir muammoni o'z ko'zimiz bilan ko'ramiz.
2.2 Katakdagi ro'yxat
"uy tegli vazifalarni toping". Teglar bitta matnda — qidiruv faqat LIKE bilan:
SELECT vazifa_id, teglar FROM t35_yassi WHERE teglar LIKE '%uy%'; vazifa_id | teglar
-----------+-----------
1 | bozor,uy
2 | ta'mir,uy
4 | uyqu
(3 rows)4-vazifa ortiqcha: uyqu ichida ham "uy" bor. Vergullarni hisobga olib murakkab shablon yozish mumkin, lekin bu kasallikni emas, alomatni davolaydi. Teg nomini o'zgartirish ("uy" → "uy-joy") esa har qatordagi matnni qayta yozishni talab qiladi.
2.3 Update anomaliyasi
Anomaliya (anomaly) — ma'lumotni o'zgartirishda bazaning o'ziga zid holatga tushishi. Aziz Chirchiqqa ko'chdi. Biz shaharni faqat bitta qatorda yangiladik:
UPDATE t35_yassi SET egasi_shahar = 'Chirchiq' WHERE vazifa_id = 1;
SELECT DISTINCT egasi_email, egasi_shahar
FROM t35_yassi
WHERE egasi_email = 'aziz@example.com';UPDATE 1
egasi_email | egasi_shahar
------------------+--------------
aziz@example.com | Chirchiq
aziz@example.com | Toshkent
(2 rows)Endi baza "Aziz ikki shaharda yashaydi" deydi. Qaysi biri to'g'ri — hech kim bilmaydi. Bu update anomaliyasi: bitta fakt bir necha joyda, va biz hammasini yangilashni unutdik. Dasturda bu "ba'zi sahifalarda eski manzil" ko'rinishida chiqadi.
2.4 Delete anomaliyasi
Dilnoza ikkala vazifasini bajarib, o'chirdi:
DELETE FROM t35_yassi WHERE egasi_email = 'dilnoza@example.com';
SELECT count(*) AS dilnoza_haqida
FROM t35_yassi
WHERE egasi_email = 'dilnoza@example.com';DELETE 2
dilnoza_haqida
----------------
0
(1 row)Vazifalar bilan birga Dilnozaning o'zi ham yo'qoldi — ismi, shahri, emaili. Biz faqat vazifalarni o'chirmoqchi edik. Bu delete anomaliyasi: bir faktni o'chirish boshqa, bog'liq bo'lmagan faktni ham olib ketadi.
2.5 Insert anomaliyasi
Shahnoza ro'yxatdan o'tdi, lekin hali vazifa yozmadi. Uni qanday saqlaymiz?
INSERT INTO t35_yassi (egasi_email, egasi_ism)
VALUES ('shahnoza@example.com', 'Shahnoza');ERROR: null value in column "vazifa_id" of relation "t35_yassi" violates not-null constraint
DETAIL: Failing row contains (null, null, shahnoza@example.com, Shahnoza, null, null).Tarjimasi: "vazifa_id ustunidagi NULL qiymat NOT NULL cheklovini buzadi". Birlamchi kalit vazifa raqami — vazifasiz qator bo'lishi mumkin emas. Bu insert anomaliyasi: bir faktni (foydalanuvchi bor) boshqa fakt (vazifa) bo'lmaguncha yozib bo'lmaydi.
Tekshirib ko'ring: Uchala anomaliyaning umumiy sababi nima?
Javob
Bitta jadvalda ikki xil "narsa" haqidagi fakt aralash: vazifalar haqida va foydalanuvchilar haqida. Foydalanuvchi fakti vazifa qatoriga "osilib" qolgan — shuning uchun u takrorlanadi (update), vazifa bilan birga o'chadi (delete) va vazifasiz yozilmaydi (insert). Davosi — har "narsa"ga o'z jadvali.
3. Funksional bog'liqlik
3.1 Ta'rif
Normal formalarning hammasi bitta tushunchaga tayanadi. Funksional bog'liqlik (functional dependency) — A ustun qiymatini bilsangiz, B ustun qiymati aniq bitta bo'ladi. Yoziladi: A → B ("A B ni aniqlaydi").
Misollar:
egasi_email → egasi_ism— bitta emailning egasi bitta odam, ismi bitta.vazifa_id → matn— bitta vazifaning matni bitta.egasi_shahar → egasi_email— yo'q: Toshkentda ko'p odam yashaydi.
O'xshatish: pasport seriyasi va raqami. Raqamni bilsangiz — egasining tug'ilgan sanasi aniq bitta. Lekin tug'ilgan sanani bilsangiz — pasport raqamini topa olmaysiz, chunki o'sha kuni minglab odam tug'ilgan. Bog'liqlik bir tomonga ishlaydi.
3.2 So'rov bilan tekshirish
Funksional bog'liqlik — biznes qoidasi, uni hujjatdan yoki buyurtmachidan bilasiz. Lekin mavjud ma'lumotda u buzilganmi — buni so'rov ko'rsatadi: bitta A qiymatiga bir nechta B to'g'ri kelsa, A → B buzilgan. Kanon so'rovi (update anomaliyasidan keyingi holatda):
SELECT egasi_email, count(DISTINCT egasi_shahar) AS shaharlar
FROM t35_yassi
GROUP BY egasi_email
HAVING count(DISTINCT egasi_shahar) > 1; egasi_email | shaharlar
------------------+-----------
aziz@example.com | 2
(1 row)egasi_email → egasi_shahar bo'lishi kerak edi, lekin ma'lumotda u buzilgan — bitta emailga ikki shahar. Bu so'rov shaklini eslab qoling: GROUP BY A HAVING count(DISTINCT B) > 1. U eski, "iflos" bazani tozalashda birinchi ishlatiladigan vosita.
4. Normal formalar zinapoyasi
Normal forma (normal form) — jadval qaysi takrorlanish turlaridan xoli ekanini bildiruvchi daraja. Har keyingi forma oldingisini o'z ichiga oladi: 3NF dagi jadval 2NF va 1NF da ham. Nimaga qarang: har zinapoyada bitta savol.
flowchart TB
A["1NF: har katakda bitta qiymat"] --> B["2NF: kalitning bir qismiga bog'liq ustun yo'q"]
B --> C["3NF: kalit bo'lmagan ustunga bog'liq ustun yo'q"]
C --> D["BCNF: har aniqlovchi — kalit"]4.1 1NF — atomar qiymatlar
Birinchi normal forma (1NF): har katakda bitta qiymat va takrorlanadigan ustunlar guruhi yo'q.
Buzilish belgilari:
- katakda ro'yxat:
teglar = 'bozor,uy'; - raqamli ustunlar:
telefon1,telefon2,telefon3.
Yechim — ro'yxat alohida jadvalga, har element — alohida qator (N:M ko'prik jadval). Bizning t35_yassi 1NF ni teglar ustuni tufayli buzadi.
Diqqat: Postgres massiv (
text[]) vajsonbturlarini qo'llab-quvvatlaydi. Ular 1NF ni "rasman" buzadi, lekin ba'zan to'g'ri tanlov: ro'yxat faqat butunligicha o'qilsa va uning elementlari bo'yicha boshqa jadvallarga bog'lanish kerak bo'lmasa. Teglar esa bunday emas — ular bo'yicha qidiramiz, sanaymiz, nomini o'zgartiramiz. Bu farqni Modellash naqshlari darsida o'lchov bilan ko'ramiz.
4.2 2NF — kalitning bir qismiga bog'liqlik yo'q
Ikkinchi normal forma (2NF): 1NF + kalit bo'lmagan har ustun butun birlamchi kalitga bog'liq, uning bir qismiga emas. Bu faqat tarkibiy kalitli jadvallarda muammo bo'ladi.
Misol: ko'prik jadvalga "qulaylik uchun" teg nomini ham yozdik. Kalit — (vazifa_id, teg_id), lekin teg_nomi faqat teg_id ga bog'liq. Biz o'z klasterimizda sinadik (tranzaksiyada, ROLLBACK bilan):
BEGIN;
CREATE TABLE t35_vt (
vazifa_id int,
teg_id int,
teg_nomi text,
PRIMARY KEY (vazifa_id, teg_id)
);
INSERT INTO t35_vt
VALUES (1, 10, 'uy'), (2, 10, 'uy'), (2, 11, 'ish');
UPDATE t35_vt SET teg_nomi = 'uy-joy' WHERE vazifa_id = 1;
SELECT teg_id, count(DISTINCT teg_nomi) AS nomlar
FROM t35_vt
GROUP BY teg_id
HAVING count(DISTINCT teg_nomi) > 1;
ROLLBACK;BEGIN
CREATE TABLE
INSERT 0 3
UPDATE 1
teg_id | nomlar
--------+--------
10 | 2
(1 row)
ROLLBACKXuddi o'sha update anomaliyasi: 10-tegning ikki nomi bor. teg_id → teg_nomi bog'liqligi kalitning bir qismidan chiqadi — 2NF buzilgan. Yechim: teg_nomi teglar jadvaliga ko'chadi, u yerda kalit to'liq teg_id. Bizning vazifa_teglari aynan shunday: faqat ikki ustun.
4.3 3NF — tranzitiv bog'liqlik yo'q
Uchinchi normal forma (3NF): 2NF + kalit bo'lmagan ustun boshqa kalit bo'lmagan ustunga bog'liq emas. Bunday zanjir tranzitiv bog'liqlik (transitive dependency) deyiladi: vazifa_id → egasi_email → egasi_ism.
t35_yassi da kalit — vazifa_id (bitta ustun), shuning uchun 2NF buzilishi yo'q. Lekin egasi_ism va egasi_shahar vazifaga emas, emailga bog'liq — 3NF buzilgan. Update anomaliyasining manbai shu edi.
Qisqa eslab qolish formulasi (inglizcha "kalitga, butun kalitga va faqat kalitga"): har ustun kalitga bog'liq (1NF), butun kalitga (2NF) va faqat kalitga (3NF).
4.4 BCNF — har aniqlovchi kalit bo'lsin
Boyce–Codd normal formasi (BCNF): har funksional bog'liqlik A → B da A — kalit (yoki kalitni o'z ichiga oladi). 3NF dan farqi nozik va kamdan-kam uchraydi: bir nechta ustma-ust tushadigan kalitlar bo'lganda.
Klassik misol — dars jadvali. Qoidalar: har talaba har fandan bitta ustozda o'qiydi; har ustoz faqat bitta fanni o'tadi. Biz o'z klasterimizda sinadik:
BEGIN;
CREATE TABLE t35_dars (
talaba text,
fan text,
ustoz text,
PRIMARY KEY (talaba, fan)
);
INSERT INTO t35_dars VALUES
('Ali', 'Fizika', 'Jasur aka'),
('Malika', 'Fizika', 'Jasur aka'),
('Ali', 'Matematika', 'Nodira opa');
SELECT ustoz, count(DISTINCT fan) AS fanlar, count(*) AS qatorlar
FROM t35_dars
GROUP BY ustoz
ORDER BY ustoz;
ROLLBACK;BEGIN
CREATE TABLE
INSERT 0 3
ustoz | fanlar | qatorlar
------------+--------+----------
Jasur aka | 1 | 2
Nodira opa | 1 | 1
(2 rows)
ROLLBACKustoz → fan bog'liqligi bor ("Jasur aka — fizika ustozi"), lekin ustoz kalit emas. Natija: "Jasur aka fizikadan dars beradi" fakti ikki qatorda (har talaba uchun). Yangi ustozni talabasi bo'lmaguncha yozib bo'lmaydi — yana insert anomaliyasi. Bu jadval 3NF da (chunki fan kalitning qismi), lekin BCNF da emas. Yechim: ustozlar (ustoz PK, fan) va darslar (talaba, ustoz).
Amalda 3NF ga yetgan sxemaning ko'pchiligi BCNF da ham bo'ladi. 4NF va 5NF ham bor — ular juda kam uchraydigan holatlar (bir jadvalda ikki mustaqil ro'yxat) uchun. Intervyuda odatda 3NF gacha so'raladi, BCNF — "qo'shimcha ball".
Tekshirib ko'ring:
buyurtma_qatorlari (buyurtma_id, mahsulot_id, soni, mahsulot_nomi), kalit —(buyurtma_id, mahsulot_id). Qaysi forma buziladi va nega?
Javob
2NF. mahsulot_nomi faqat mahsulot_id ga bog'liq — kalitning bir qismiga. soni esa butun kalitga bog'liq (qaysi buyurtmada qaysi mahsulotdan nechta) — u joyida. mahsulot_nomi mahsulotlar jadvalida bo'lishi kerak.
5. 3NF ga ko'chirish
Endi t35_yassi ni to'g'ri jadvallarga bo'lamiz. Har "narsa" — o'z jadvali: foydalanuvchi, vazifa, teg va ko'prik. Kanon tajribasi update anomaliyasidan keyingi holatdan davom etadi (Aziz "Chirchiq", Dilnoza o'chirilgan):
CREATE TABLE t35_foydalanuvchi (
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text UNIQUE NOT NULL,
ism text NOT NULL,
shahar text
);
CREATE TABLE t35_vazifa (
id int PRIMARY KEY,
egasi_id int NOT NULL REFERENCES t35_foydalanuvchi,
matn text NOT NULL
);
CREATE TABLE t35_teg (
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom text UNIQUE NOT NULL
);
CREATE TABLE t35_vazifa_teg (
vazifa_id int REFERENCES t35_vazifa,
teg_id int REFERENCES t35_teg,
PRIMARY KEY (vazifa_id, teg_id)
);CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLEMa'lumotni ko'chirish — INSERT … SELECT (INSERT va RETURNING). Foydalanuvchilarni DISTINCT ON bilan bittadan olamiz (SELECT va WHERE) — har email uchun eng kichik vazifa_id li qator. Bu yerda bir qaror bor: Azizning ikki shahridan biri tanlanadi (1-vazifadagi — "Chirchiq"). Haqiqiy loyihada bunday ziddiyatni odam hal qiladi.
INSERT INTO t35_foydalanuvchi (email, ism, shahar)
SELECT DISTINCT ON (egasi_email) egasi_email, egasi_ism, egasi_shahar
FROM t35_yassi
ORDER BY egasi_email, vazifa_id;
INSERT INTO t35_vazifa
SELECT y.vazifa_id, f.id, y.matn
FROM t35_yassi y
JOIN t35_foydalanuvchi f ON f.email = y.egasi_email;INSERT 0 1
INSERT 0 2Teglar katakdagi ro'yxatdan keladi. string_to_array matnni massivga bo'ladi, unnest massivni qatorlarga yoyadi:
INSERT INTO t35_teg (nom)
SELECT DISTINCT unnest(string_to_array(teglar, ','))
FROM t35_yassi
ORDER BY 1;
INSERT INTO t35_vazifa_teg
SELECT y.vazifa_id, t.id
FROM t35_yassi y
CROSS JOIN unnest(string_to_array(y.teglar, ',')) AS u(nom)
JOIN t35_teg t ON t.nom = u.nom;INSERT 0 3
INSERT 0 4Uchta teg (bozor, ta'mir, uy) va to'rtta bog'lanish. Natijani birlashtirib tekshiramiz — u yassi jadvaldagi ma'lumot bilan mos bo'lishi kerak:
SELECT v.id, v.matn, f.ism, f.shahar,
string_agg(t.nom, ',' ORDER BY t.nom) AS teglar
FROM t35_vazifa v
JOIN t35_foydalanuvchi f ON f.id = v.egasi_id
JOIN t35_vazifa_teg vt ON vt.vazifa_id = v.id
JOIN t35_teg t ON t.id = vt.teg_id
GROUP BY v.id, f.id
ORDER BY v.id; id | matn | ism | shahar | teglar
----+----------------------+--------------+----------+-----------
1 | Bozordan kartoshka | Aziz Karimov | Chirchiq | bozor,uy
2 | Jo'mrak almashtirish | Aziz Karimov | Chirchiq | ta'mir,uy
(2 rows)Endi uch anomaliyani qayta sinaymiz. Shahar — bitta joyda, bitta UPDATE. Vazifasiz foydalanuvchi — muammosiz yoziladi:
UPDATE t35_foydalanuvchi SET shahar = 'Toshkent'
WHERE email = 'aziz@example.com';
INSERT INTO t35_foydalanuvchi (email, ism)
VALUES ('shahnoza@example.com', 'Shahnoza')
RETURNING id;UPDATE 1
id
----
2
(1 row)
INSERT 0 1UPDATE 1 — faqat bitta qator: Azizning shahri endi bitta joyda. Shahnoza vazifasiz yozildi. Vazifani o'chirish esa foydalanuvchini olib ketmaydi — ular endi alohida jadvallarda. Uch anomaliyaning hammasi yo'qoldi.
DROP TABLE t35_yassi, t35_vazifa_teg, t35_teg, t35_vazifa,
t35_foydalanuvchi;DROP TABLE6. vazifalar sxemasi qaysi formada?
Kanon sxemasini shu ko'z bilan ko'rib chiqamiz:
| Jadval | Kalit | Holat |
|---|---|---|
foydalanuvchilar |
id |
3NF/BCNF: email, ism, vaqt — faqat foydalanuvchiga tegishli |
vazifalar |
id |
3NF: egasi haqida faqat egasi_id, ismi yo'q |
teglar |
id (+ UNIQUE (egasi_id, nom)) |
BCNF |
vazifa_teglari |
(vazifa_id, teg_id) |
2NF muammosi yo'q — qo'shimcha ustun yo'q |
izohlar |
id |
3NF: muallif — faqat muallif_id |
Lekin ikki joyda ma'lumot ataylab takrorlanadi:
- Teglar ikki joyda: vazifa
matnida (#uy) vavazifa_teglarijadvalida. Frontend matnni ko'rsatadi, qidiruv esa jadvaldan. Holat 34 da ularni hech narsa sinxronlamaydi: jadval UPSERT va MERGE darsida bir marta to'ldirilgan, matn o'zgarsa u eskiradi. Ikkalasini bir xil saqlaydigan trigger'ni keyingi darsda qo'shamiz. - Izohlar soni (
izohlar_soni) — keyingi holatda qo'shiladi: har vazifa yonida tayyor son, har safarcount(*)qilmaslik uchun.
Bu — denormalizatsiya: tezlik yoki qulaylik uchun normal formadan ongli chekinish. U bepul emas — sinxronlash kerak. Narxi va qachon arzishini keyingi darsda o'lchaymiz.
7. Ko'p uchraydigan xatolar
1. Hamma narsani bitta jadvalga. "Hozircha oddiy bo'lsin" deb boshlangan yassi jadval birinchi ziddiyatgacha ishlaydi. Belgisi: bir xil qiymatlar ko'p qatorda takrorlanadi (ism, shahar, kategoriya nomi).
2. Ortiqcha normalizatsiya. Har narsani alohida jadvalga: ismlar, familiyalar, shaharlar, pochta_indekslari. Har so'rov 6 ta JOIN. Agar ustunning o'zi haqida boshqa fakt saqlanmasa va u hech narsani aniqlamasa — uni alohida jadvalga chiqarishga hojat yo'q.
3. Hisoblanadigan ustunni saqlash. jami_narx ustuni — soni × narx dan kelib chiqadi. Bitta qator yangilanib, ikkinchisi unutilsa — ziddiyat. Saqlashning sababi bo'lsa (tezlik, tarixiy narx) — bu ongli denormalizatsiya, sinxronlash bilan.
4. Tarixiy qiymatni normallashtirib yuborish. Buyurtmadagi narx — buyurtma paytidagi narx. Uni mahsulotlar.narx ga JOIN qilib olsangiz, narx o'zgarganda eski buyurtmalar summasi ham o'zgaradi. Bu holda buyurtma_qatorlari.narx — takrorlash emas, boshqa fakt ("shu buyurtmadagi narx"). Keyingi darsda "snapshot" nomi bilan ko'ramiz.
5. Funksional bog'liqlikni ma'lumotdan "chiqarish". Ma'lumotda hozir har shaharda bitta odam bo'lsa, shahar → email "bajariladi" — lekin bu qoida emas, tasodif. Bog'liqlik biznes qoidasidan keladi; so'rov faqat buzilishni topadi.
8. Mashqlar
1-mashq (oson)
«Bahor» oshxonasi buyurtmalarini shunday jadvalda saqlaydi: buyurtmalar (id, mijoz_tel, mijoz_ism, taomlar), taomlar = 'osh,manti'. Qaysi normal forma(lar) buzilgan va qaysi anomaliya birinchi chiqadi? Ishora: «Normal formalar zinapoyasi» bo'limidagi har formaning savoli.
Yechim
- 1NF buzilgan:
taomlarkatagida ro'yxat. - 3NF buzilgan:
id → mijoz_tel → mijoz_ism— mijoz ismi buyurtmaga emas, telefonga bog'liq.
Birinchi chiqadigan anomaliya — update: mijoz ismini tuzatsangiz, hamma eski buyurtmalarida tuzatish kerak. Yana: "manti necha marta sotilgan?" savoliga LIKE '%manti%' bilan javob berish va narxni qayerdan olish noaniq.
2-mashq (o'rta)
Quyidagi jadvalda egasi_email → egasi_ism bog'liqligi buzilgan joylarni toping. Har email uchun turli ismlar soni (ismlar) va ularning ro'yxati (variantlar, ' | ' bilan) chiqsin. Hammasi BEGIN … ROLLBACK ichida. Ishora: «So'rov bilan tekshirish» bo'limidagi GROUP BY … HAVING count(DISTINCT …) shakli; ro'yxat — string_agg(DISTINCT …) (Agregatlar).
CREATE TABLE t35_royxat (
vazifa_id int PRIMARY KEY,
egasi_email text,
egasi_ism text
);
INSERT INTO t35_royxat VALUES
(1, 'aziz@example.com', 'Aziz Karimov'),
(2, 'aziz@example.com', 'Aziz Karimov'),
(3, 'aziz@example.com', 'Aziz Karim'),
(4, 'malika@example.com', 'Malika Sobirova');Yechim
BEGIN;
CREATE TABLE t35_royxat (
vazifa_id int PRIMARY KEY,
egasi_email text,
egasi_ism text
);
INSERT INTO t35_royxat VALUES
(1, 'aziz@example.com', 'Aziz Karimov'),
(2, 'aziz@example.com', 'Aziz Karimov'),
(3, 'aziz@example.com', 'Aziz Karim'),
(4, 'malika@example.com', 'Malika Sobirova');
SELECT egasi_email,
count(DISTINCT egasi_ism) AS ismlar,
string_agg(DISTINCT egasi_ism, ' | ') AS variantlar
FROM t35_royxat
GROUP BY egasi_email
HAVING count(DISTINCT egasi_ism) > 1;
ROLLBACK;BEGIN
CREATE TABLE
INSERT 0 4
egasi_email | ismlar | variantlar
------------------+--------+---------------------------
aziz@example.com | 2 | Aziz Karim | Aziz Karimov
(1 row)
ROLLBACK3-qatorda familiya xato yozilgan — yassi jadvalda bunday "kichik" farqlar tez-tez uchraydi. Normallashtirishdan oldin ularni odam bilan birga tuzatish kerak; aks holda DISTINCT ikki xil "Aziz"ni yaratadi.
3-mashq (qiyin)
«Bahor» buyurtmalari yassi jadvalda. Kalit — (buyurtma_id, taom). Funksional bog'liqliklarni toping va jadvalni 3NF ga bo'ling: t35_mijozlar (tel, ism), t35_taomlar (nom, narx), t35_buyurtmalar (id, mijoz_tel), t35_buyurtma_qatorlari (buyurtma_id, taom, soni) — kalitlar va tashqi kalitlar bilan. Ma'lumotni INSERT … SELECT DISTINCT bilan ko'chiring va har buyurtma summasini (jami) chiqaring. Hammasi BEGIN … ROLLBACK ichida. Ishora: «3NF ga ko'chirish» bo'limidagi tartib — avval "ota" jadvallar, keyin bolalar.
CREATE TABLE t35_buyurtma (
buyurtma_id int,
mijoz_tel text,
mijoz_ism text,
taom text,
narx int,
soni int,
PRIMARY KEY (buyurtma_id, taom)
);
INSERT INTO t35_buyurtma VALUES
(1, '+998 90 000 00 01', 'Ali', 'osh', 35000, 2),
(1, '+998 90 000 00 01', 'Ali', 'ko''k choy', 5000, 1),
(2, '+998 90 000 00 02', 'Malika', 'manti', 30000, 3),
(3, '+998 90 000 00 01', 'Ali', 'osh', 35000, 1);Yechim
Bog'liqliklar: buyurtma_id → mijoz_tel (buyurtma bitta mijozniki — kalitning bir qismiga, 2NF buzilishi), mijoz_tel → mijoz_ism (tranzitiv, 3NF), taom → narx (kalitning bir qismiga, 2NF). soni — butun kalitga bog'liq, joyida qoladi.
BEGIN;
CREATE TABLE t35_buyurtma (
buyurtma_id int,
mijoz_tel text,
mijoz_ism text,
taom text,
narx int,
soni int,
PRIMARY KEY (buyurtma_id, taom)
);
INSERT INTO t35_buyurtma VALUES
(1, '+998 90 000 00 01', 'Ali', 'osh', 35000, 2),
(1, '+998 90 000 00 01', 'Ali', 'ko''k choy', 5000, 1),
(2, '+998 90 000 00 02', 'Malika', 'manti', 30000, 3),
(3, '+998 90 000 00 01', 'Ali', 'osh', 35000, 1);
CREATE TABLE t35_mijozlar (
tel text PRIMARY KEY,
ism text NOT NULL
);
CREATE TABLE t35_taomlar (
nom text PRIMARY KEY,
narx int NOT NULL
);
CREATE TABLE t35_buyurtmalar (
id int PRIMARY KEY,
mijoz_tel text NOT NULL REFERENCES t35_mijozlar
);
CREATE TABLE t35_buyurtma_qatorlari (
buyurtma_id int REFERENCES t35_buyurtmalar,
taom text REFERENCES t35_taomlar,
soni int NOT NULL,
PRIMARY KEY (buyurtma_id, taom)
);
INSERT INTO t35_mijozlar
SELECT DISTINCT mijoz_tel, mijoz_ism FROM t35_buyurtma;
INSERT INTO t35_taomlar
SELECT DISTINCT taom, narx FROM t35_buyurtma;
INSERT INTO t35_buyurtmalar
SELECT DISTINCT buyurtma_id, mijoz_tel FROM t35_buyurtma;
INSERT INTO t35_buyurtma_qatorlari
SELECT buyurtma_id, taom, soni FROM t35_buyurtma;
SELECT b.id, m.ism, sum(q.soni * t.narx) AS jami
FROM t35_buyurtmalar b
JOIN t35_mijozlar m ON m.tel = b.mijoz_tel
JOIN t35_buyurtma_qatorlari q ON q.buyurtma_id = b.id
JOIN t35_taomlar t ON t.nom = q.taom
GROUP BY b.id, m.ism
ORDER BY b.id;
ROLLBACK;BEGIN
CREATE TABLE
INSERT 0 4
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 2
INSERT 0 3
INSERT 0 3
INSERT 0 4
id | ism | jami
----+--------+-------
1 | Ali | 75000
2 | Malika | 90000
3 | Ali | 35000
(3 rows)
ROLLBACK1-buyurtma: 2 × 35 000 + 1 × 5 000 = 75 000 so'm. Bitta nozik joy: narx endi t35_taomlar da — osh qimmatlasa, eski buyurtmalar summasi ham "o'zgaradi". Buyurtma tarixini to'g'ri saqlash uchun narx buyurtma qatorida ham bo'lishi kerak (buyurtma paytidagi narx) — bu keyingi darsdagi "snapshot" naqshi.
9. SQL qadami
Bu dars — faqat tajriba: kanon sxemasi o'zgarmaydi, holat 34 da qolamiz. Kanon tajribasi alohida bo'sh bazada (tajriba) bajarilgan va oxirida jadvallarini o'chirgan.
Sxemaga o'zgarish kerak emas, chunki vazifalar boshidan 3NF da loyihalangan: foydalanuvchi haqidagi fakt — foydalanuvchilar da, teglar — alohida jadval va ko'prikda, izoh muallifi — faqat raqam. Holat 34 sxemasini «vazifalar sxemasi qaysi formada?» bo'limidagi jadval bilan o'zingiz tekshiring: har jadvalda "bu ustun kalitga, butun kalitga va faqat kalitga bog'liqmi?" savolini bering.
Commit yo'q.
10. Real ishda
- Excel'dan bazaga ko'chirish. Kichik biznesni avtomatlashtirishning birinchi ishi — aynan shu darsdagi jarayon: yassi jadval → bog'liqliklarni topish → ziddiyatlarni tozalash →
INSERT … SELECT DISTINCT. - Code review. "Nega bu ustun bu jadvalda?" — sxema review'ining asosiy savoli. 3NF tili jamoada bir-birini tez tushunishga yordam beradi.
- Analitika bazalari (ma'lumotlar ombori, data warehouse) ataylab denormallashtiriladi — yozish kam, o'qish ko'p. Ilova bazasi (OLTP) esa odatda 3NF ga yaqin.
- Intervyuda: "1NF, 2NF, 3NF ni misol bilan tushuntiring", "anomaliyalar qanday bo'ladi?" — juda tez-tez beriladigan savol. Javobda doim misol keltiring.
Xulosa
- Bir fakt bir necha joyda bo'lsa — update, insert va delete anomaliyalari paydo bo'ladi.
- Funksional bog'liqlik
A → B: A bilan B aniq bitta; buzilishiniGROUP BY A HAVING count(DISTINCT B) > 1topadi. - 1NF — katakda bitta qiymat; 2NF — butun kalitga bog'liq; 3NF — faqat kalitga bog'liq; BCNF — har aniqlovchi kalit.
- Yassi jadval
INSERT … SELECT DISTINCT,DISTINCT ON,string_to_array+unnestbilan bo'linadi. vazifalar— 3NF; ikki ongli istisno (matndagi teglar, izohlar soni) — denormalizatsiya.
Keyingi dars: Denormalizatsiya va hisoblangan maydonlar — qachon ataylab takrorlash arziydi, counter cache, snapshot va trigger bilan sinxronlash.
Manbalar
- E. F. Codd, "Further Normalization of the Data Base Relational Model", 1971 (2NF, 3NF); E. F. Codd, "Recent Investigations in Relational Data Base Systems", 1974 (BCNF — R. Boyce bilan).
- PostgreSQL 18 hujjati: "Array Functions" (
string_to_array,unnest) va "SELECT — DISTINCT ON" — postgresql.org/docs/18, 2026-10-24 da ko'rildi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!