09 β Logik modeldan fizik sxemaga¶
β¬ οΈ Oldingi: 08 β Normalizatsiya II: BCNF, 4NF, 5NF va denormalizatsiya Β· π README Β· Keyingi: 10 β To'g'ri ma'lumot turini tanlash (PostgreSQL boy turlari) β‘οΈ
Bu bobda: ER/logik modelni qog'ozdan haqiqiy PostgreSQL jadvallariga aylantiramiz. Har bir element uchun aniq o'tkazish qoidasini ko'ramiz: entity -> jadval, atribut -> ustun, 1:N -> bola tomonda FK, N:M -> bog'lovchi (junction) jadval, 1:1 -> qaysi tomonda FK, kuchsiz entity -> kompozit kalit. Keyin izchil nomlash konvensiyasini (snake_case), DDL skript tartibini (avval ota jadval) va PG schema/
search_pathbilan tashkil etishni o'rganamiz. Yakunda butun bir do'kon modelini noldan to'liq DDL ga aylantiramiz va uni jonli PostgreSQL 18 klasterida ishga tushiramiz.
0. Ko'prik: II qismdan III qismga¶
Bu bob kitobning ikki yarmini bog'laydigan ko'prik. Shu paytgacha (II qism) biz modellashtirish bilan shug'ullandik: ER-diagramma chizdik (03-bob), kardinallikni aniqladik (04-bob), kalit tanladik (06-bob) va normalizatsiya qildik (07β08-boblar). Bularning hammasi β logik model: ma'lumot qanday tashkil etilishi, mahsulot (DBMS) ga bog'liq emas.
Endi (III qismdan boshlab) biz fizik dizayn ga o'tamiz: bu logik modelni aynan PostgreSQL'da CREATE TABLE skriptiga aylantirish. Bu mexanik, lekin qoidaga asoslangan jarayon. Qoidalarni bir marta o'rgansangiz β har qanday ER-modelni ishonchli DDL ga aylantira olasiz.
Eslatma:
CREATE TABLE,FOREIGN KEY,PRIMARY KEYning sintaksisi sizga tanish (SQL kitobining 4-bobiga qarang). Bu bob sintaksisni qayta o'rgatmaydi β u qaysi konstruksiyani qachon va nega tanlashni o'rgatadi. Ya'ni dizayn qarori, sintaksis emas.
Uch bosqichli dizayn jarayonini (01-bobdan) eslang: konseptual -> logik -> fizik. Biz hozir oxirgi o'qning ustidamiz.
1. O'tkazishning oltita qoidasi¶
Logik modelda atigi bir nechta element turi bor: entity, atribut va bog'lanish (1:1, 1:N, N:M). Har biri uchun aniq bir o'tkazish qoidasi mavjud. Quyidagi xarita butun bobning xulosasi:
Endi har bir qoidani alohida, do'kon domeni misolida ko'ramiz.
1.1 Qoida 1 β Entity -> jadval¶
Har bir kuchli (mustaqil) entity bitta CREATE TABLE ga aylanadi. Jadvalga surrogate birlamchi kalit (id) qo'shamiz β buni 06-bobda muhokama qilgan edik (surrogate kalit barqaror va o'zgarmas).
GENERATED ALWAYS AS IDENTITY β PostgreSQL'da surrogate kalit yaratishning zamonaviy, standart-SQL usuli (eski serial o'rniga afzal). bigint ni int dan ko'ra tanlaymiz, chunki jadval o'sib 2 milliarddan oshib ketishi mumkin va keyin turni o'zgartirish og'riqli.
1.2 Qoida 2 β Atribut -> ustun¶
Entity atributlari jadval ustunlariga aylanadi. Bu yerda ikki qaror bor:
- Tur (type) β
text,numeric,timestamptzva h.k. To'g'ri tur tanlash o'z-o'zicha katta dizayn mavzusi; uni to'liq 10-bobda ko'ramiz. Hozircha asosiy tanlovlarni ishlatamiz: matn uchuntext, pul uchunnumeric(12,2)(hech qachonfloatemas!), vaqt uchuntimestamptz. - Majburiymi? β Logik modelda atribut majburiy (mandatory) bo'lsa, ustunga
NOT NULLqo'yamiz. Ixtiyoriy bo'lsa βNOT NULLsiz qoldiramiz (NULL ruxsat).
CREATE TABLE mijoz (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL, -- majburiy atribut
ismi text NOT NULL, -- majburiy
yaratilgan timestamptz NOT NULL DEFAULT now()
);
Kompozit/ko'p qiymatli/hosil atribut. Logik modeldagi maxsus atribut turlari (03-bobni eslang) fizik sxemada quyidagicha hal qilinadi: kompozit atribut (masalan,
manzil = shahar + ko'cha + uy) odatda alohida ustunlarga yoyiladi; ko'p qiymatli atribut (masalan, bir mijozning bir necha telefoni) alohida jadvalga ajratiladi (1NF talabi β 07-bob); hosil atribut (masalan,jami = soni * narx) generated column bo'lib saqlanadi (08-bobni eslang).
1.3 Qoida 3 β 1:N -> bola tomonda FK¶
Bu eng ko'p uchraydigan bog'lanish. Qoida sodda: FK ustunini "ko'p" (N) tomondagi jadvalga qo'y, va u "bir" (1) tomonning birlamchi kalitiga ishora qilsin.
Do'konda: bitta kategoriya ga ko'p mahsulot tegishli (1:N). Demak FK mahsulot jadvalida:
CREATE TABLE mahsulot (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
kategoriya_id bigint NOT NULL,
nomi text NOT NULL,
narx numeric(12,2) NOT NULL,
CONSTRAINT fk_mahsulot_kategoriya
FOREIGN KEY (kategoriya_id) REFERENCES kategoriya (id)
ON UPDATE CASCADE ON DELETE RESTRICT
);
Ikki nozik nuqta:
NOT NULLmi? Agar har bir mahsulot majburan bir kategoriyaga tegishli bo'lsa (majburiy ishtirok) ->NOT NULL. Agar mahsulot kategoriyasiz ham bo'la olsa (ixtiyoriy ishtirok, "modallik" β 04-bob) -> NULL ruxsat.ON DELETEstrategiyasi. Ota o'chirilsa bola bilan nima bo'ladi?RESTRICT(bola bo'lsa o'chirishni rad et),CASCADE(bolalarni ham o'chir),SET NULL(FK ni NULL qil). Bu dizayn qarori β uni to'liq 11-bobda ko'ramiz. Bu yerdaRESTRICTtanladik: tegishli mahsulotlari bor kategoriyani tasodifan o'chirib bo'lmasin.
O'z-o'ziga 1:N (rekursiv bog'lanish). Xodim-boshliq kabi holatda FK o'sha jadvalning o'ziga ishora qiladi:
boshliq_id bigint REFERENCES xodim(id). Bu ham 1:N ning bir ko'rinishi. Daraxt strukturalarini (17-bob) chuqurroq ko'ramiz.
1.4 Qoida 4 β N:M -> bog'lovchi (junction) jadval¶
Relyatsion modelda N:M bog'lanishni to'g'ridan-to'g'ri ifodalab bo'lmaydi β har doim uchinchi jadval (junction / bog'lovchi / o'tish jadvali) kerak. Bu jadval ikki tomonning kalitlarini bir-biriga ulaydi.
Do'konda: bitta buyurtma da ko'p mahsulot, bitta mahsulot ko'p buyurtma da bo'ladi (N:M). Bog'lovchi jadval β buyurtma_satri:
CREATE TABLE buyurtma_satri (
buyurtma_id bigint NOT NULL REFERENCES buyurtma (id) ON DELETE CASCADE,
mahsulot_id bigint NOT NULL REFERENCES mahsulot (id) ON DELETE RESTRICT,
soni integer NOT NULL,
narx_snapshot numeric(12,2) NOT NULL,
PRIMARY KEY (buyurtma_id, mahsulot_id),
CONSTRAINT ck_satr_soni_musbat CHECK (soni > 0)
);
Diqqat qiling:
- Birlamchi kalit kompozit:
PRIMARY KEY (buyurtma_id, mahsulot_id)β ikki FK birgalikda kalit bo'ladi. Bu bir buyurtmada bir mahsulot ikki marta yozilmasligini ham kafolatlaydi. - Bog'lanish atributlari: N:M ning ko'p chiroyli tomoni β bog'lanishning o'ziga xos atributlari (bu yerda
sonivanarx_snapshot) aynan shu junction jadvalga tabiiy joylashadi. (narx_snapshotβ buyurtma paytidagi muzlatilgan narx, 08-bobdagi snapshot naqshi.)
Surrogate
idqo'shsam-chi? Ba'zan junction jadvalga ham alohidaid bigintqo'shiladi (masalan, ORM talabi yoki junction satriga boshqa jadval ishora qilishi kerak bo'lsa). U holda(buyurtma_id, mahsulot_id)gaUNIQUEqo'yib, takrorni baribir taqiqlash kerak β aks holda bitta mahsulot bir buyurtmada ikki marta paydo bo'lishi mumkin.
1.5 Qoida 5 β 1:1 -> qaysi tomonda FK?¶
1:1 eng kam uchraydigan, lekin eng ko'p qaror talab qiladigan holat. Asosiy savol: FK qaysi tomonga qo'yiladi? Bir nechta yondashuv bor.
(a) Ixtiyoriy tomonga FK + UNIQUE. Agar bog'lanish bir tomonda majburiy, ikkinchi tomonda ixtiyoriy bo'lsa β FK ni ixtiyoriy tomonga qo'ying. Do'konda: har mijoz ning profili bo'lishi shart emas, lekin har mijoz_profil aynan bitta mijozga tegishli. Demak FK mijoz_profil da:
CREATE TABLE mijoz_profil (
mijoz_id bigint PRIMARY KEY REFERENCES mijoz (id) ON DELETE CASCADE,
bio text,
avatar_url text
);
Bu yerda chiroyli hiyla: FK ustunini bir vaqtning o'zida birlamchi kalit qildik (mijoz_id ... PRIMARY KEY). PK avtomatik UNIQUE bo'lgani uchun, bitta mijozga bir nechta profil yozib bo'lmaydi β bu aynan 1:1 ni kafolatlaydi.
(b) Alohida id + UNIQUE FK. Agar jadvalga o'z surrogate kaliti kerak bo'lsa, FK ni UNIQUE qilamiz:
-- Muqobil: 1:1 ni UNIQUE FK bilan
CREATE TABLE mijoz_sozlama (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
mijoz_id bigint NOT NULL UNIQUE REFERENCES mijoz (id) ON DELETE CASCADE,
til text NOT NULL DEFAULT 'uz'
);
UNIQUE bo'lmasa, bu 1:N ga aylanib qoladi. Aynan UNIQUE 1:1 ni majburlaydi.
Umuman alohida jadval kerakmi? Ko'pincha 1:1 atributlarini shunchaki asosiy jadvalga ustun qilib qo'shish to'g'riroq. 1:1 ni alohida jadvalga ajratish faqat shu hollarda mantiqiy: (1) ixtiyoriy/kam to'ldiriladigan ustunlarni ajratib, asosiy jadvalni ixcham saqlash; (2) maxfiy ma'lumotni (parol, hujjat) alohida ruxsat bilan himoyalash; (3) juda katta ustunni (BLOB, uzun matn) "dangasa yuklash" uchun ajratish. Sababsiz 1:1 ajratish β ortiqcha JOIN.
1.6 Qoida 6 β Kuchsiz entity -> kompozit kalit¶
Kuchsiz entity (03-bob) β egasiz mustaqil mavjud bo'lolmaydigan obyekt; uning identifikatsiyasi egasiga bog'liq. Misol: mijoz_telefon. Telefon raqami o'z-o'zicha emas, balki qaysi mijozning raqami ekani bilan ma'noli.
Fizik sxemada: kuchsiz entity jadvalining birlamchi kaliti eganing kaliti + o'zining qisman (partial) kaliti dan tashkil topadi:
CREATE TABLE mijoz_telefon (
mijoz_id bigint NOT NULL REFERENCES mijoz (id) ON DELETE CASCADE,
raqam text NOT NULL,
turi text NOT NULL DEFAULT 'asosiy',
PRIMARY KEY (mijoz_id, raqam)
);
Ikki muhim belgi:
- Kompozit PK:
(mijoz_id, raqam)βmijoz_id(ega kaliti) +raqam(qisman kalit). Bu bir mijozda bir raqam ikki marta bo'lmasligini kafolatlaydi. ON DELETE CASCADE: ega (mijoz) o'chirilsa, uning telefonlari ham avtomatik o'chadi. Bu deyarli har doim to'g'ri kuchsiz entity uchun β chunki egasiz telefon ma'nosiz.
2. Nomlash konvensiyasi β kelishuvni bir marta tanlang¶
Sxema o'sib borgani sayin, izchil nomlash uning eng kuchli "hujjati" ga aylanadi. Yangi dasturchi mahsulot.kategoriya_id ni ko'rsa, hech qanday hujjatsiz, bu kategoriya jadvaliga FK ekanini darhol tushunadi. Izchilliksiz nom esa β doimiy chalkashlik.
Eng muhimi: qaysi konvensiyani tanlashingiz aslida muhim emas, izchillik muhim. Bir loyihada bitta qoidani tanlang va undan og'ishmang.
2.1 Jadval va ustun nomi: snake_case¶
PostgreSQL nomlarni kichik harfga keltiradi (qo'shtirnoq ichida yozmasangiz). Shuning uchun CamelCase PG'da yomon ishlaydi β snake_case (kichik harf, so'zlar pastki chiziq bilan) standart:
- To'g'ri:
buyurtma_satri,kategoriya_id,yaratilgan_vaqt - Yomon:
BuyurtmaSatri(PGbuyurtmasatriga aylantiradi),"BuyurtmaSatri"(har joyda qo'shtirnoq kerak β og'riq).
2.2 Birlik mi, ko'plik mi? (jadval nomi munozarasi)¶
Bu eng mashhur "diniy urush". Ikki maktab bor:
- Birlik (
mijoz,buyurtma): "jadval β entity turi". Bir qator = bitta mijoz. ORM lar ko'pincha buni afzal ko'radi. - Ko'plik (
mijozlar,buyurtmalar): "jadval β qatorlar to'plami".SELECT * FROM mijozlartabiiyroq o'qiladi.
Maslahat: ikkalasi ham to'g'ri. Bitta loyihada bittasini tanlang. Bu kitobda birlik ishlatamiz (mijoz, mahsulot) β chunki u entity nomi bilan to'g'ridan-to'g'ri mos keladi va junction jadvallarda (buyurtma_satri) tabiiyroq.
2.3 FK ustun nomi: <ota_jadval>_id¶
FK ustunini har doim <ishora_qilingan_jadval>_id deb nomlang. Bu nom o'zi hujjat bo'ladi:
kategoriya_id->kategoriya(id)ga FKmijoz_id->mijoz(id)ga FK
Agar bitta jadval bir xil ota jadvalga ikki FK qo'ysa, rolni qo'shing: yuboruvchi_id, qabul_qiluvchi_id (ikkalasi ham foydalanuvchi(id) ga).
2.4 Constraint va indeks nomlash siyosati¶
PostgreSQL nom bermasangiz constraint'larga avtomatik nom beradi (masalan, buyurtma_satri_buyurtma_id_fkey). Bu ishlaydi, lekin muhim constraint'larga o'zingiz aniq nom bering β chunki: (1) xato xabarida o'qiladigan nom chiqadi; (2) keyin ALTER TABLE ... DROP CONSTRAINT <nom> qilish oson; (3) migratsiyalarda barqaror nom kerak.
Keng tarqalgan prefiks siyosati:
| Obyekt | Prefiks | Naqsh | Misol |
|---|---|---|---|
| Primary key | pk_ |
pk_<jadval> |
pk_mijoz |
| Unique | uq_ |
uq_<jadval>_<ustun> |
uq_mijoz_email |
| Foreign key | fk_ |
fk_<jadval>_<ota> |
fk_mahsulot_kategoriya |
| Check | ck_ |
ck_<jadval>_<qoida> |
ck_mahsulot_narx_musbat |
| Index | ix_ |
ix_<jadval>_<ustun> |
ix_buyurtma_mijoz_id |
Bu siyosatni qo'llaganda \d chiqishida farq aniq ko'rinadi (quyida 4-bo'limda haqiqiy chiqishni ko'ramiz): o'zingiz nomlagan fk_mahsulot_kategoriya va PG avtomatik bergan buyurtma_satri_buyurtma_id_fkey yonma-yon turadi.
3. DDL skript tuzilishi va tartibi¶
Endi alohida qoidalarni bitta bajariladigan skript ga yig'amiz. Bu yerda eng muhim narsa β tartib.
3.1 Oltin qoida: avval ota, keyin bola¶
FOREIGN KEY (...) REFERENCES kategoriya (id) yozilganda, kategoriya jadvali allaqachon mavjud bo'lishi shart. Demak yaratish tartibi bog'liqlik grafiga bo'ysunadi:
- Mustaqil (ota) jadvallar β hech kimga FK qo'ymaydiganlar:
kategoriya,mijoz. - Bola jadvallar β ota mavjud bo'lgandan keyin:
mahsulot(kategoriyaga),buyurtma(mijozga). - Junction va kuchsiz β ikkala otasi tayyor bo'lgach:
buyurtma_satri,mijoz_telefon. - Indekslar oxirida.
Sirkulyar bog'lanish bo'lsa-chi? Ba'zan A -> B va B -> A bir-biriga ishora qiladi (masalan,
bo'lim.boshliq_id -> xodimvaxodim.bo'lim_id -> bo'lim). U holda tartib yetmaydi: avval jadvallarni FK'siz yarating, keyinALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY ...bilan FK qo'shing. YokiDEFERRABLEconstraint ishlating (11-bob).
3.2 FK ni jadval ichida yoki ALTER bilan?¶
Ikki uslub bor, ikkalasi ham to'g'ri:
-- (a) Jadval ta'rifida (inline) β o'qish oson, lekin tartib muhim
CREATE TABLE mahsulot (
...,
kategoriya_id bigint NOT NULL REFERENCES kategoriya (id)
);
-- (b) Keyin ALTER bilan β tartibdan ozod, migratsiyalarda qulay
ALTER TABLE mahsulot
ADD CONSTRAINT fk_mahsulot_kategoriya
FOREIGN KEY (kategoriya_id) REFERENCES kategoriya (id);
Boshlang'ich skriptda (a) ko'proq o'qiladi. Mavjud bazaga o'zgartirish kiritganda (migratsiya β 23-bob) (b) afzal.
3.3 FK ustunlariga indeks β unutmang!¶
PostgreSQL avtomatik PRIMARY KEY va UNIQUE constraint'larga indeks yaratadi, lekin FK ustunlariga emas. Bu juda keng tarqalgan unutilish. FK ustuniga indeks bo'lmasa: (1) JOIN sekin bo'ladi; (2) ota qatorni o'chirish/yangilash juda sekinlashadi (PG butun bola jadvalni skanerlaydi). Shuning uchun har FK ustuniga indeks qo'shing:
CREATE INDEX ix_mahsulot_kategoriya_id ON mahsulot (kategoriya_id);
CREATE INDEX ix_buyurtma_mijoz_id ON buyurtma (mijoz_id);
CREATE INDEX ix_satr_mahsulot_id ON buyurtma_satri (mahsulot_id);
(buyurtma_satri.buyurtma_id ga alohida indeks shart emas β u kompozit PK ning birinchi ustuni, shuning uchun PK indeksi uni allaqachon qoplaydi. Indeks strategiyasini 14-bobda chuqur ko'ramiz.)
4. PostgreSQL: schema va search_path bilan tashkil etish¶
PostgreSQL'da schema β bu baza ichidagi nomlar makoni (namespace), jadvallar uchun "papka" kabi. Standart schema β public. Lekin yirik loyihada jadvallarni mantiqiy schema'larga bo'lish foydali: shop, auth, billing va h.k.
4.1 Schema yaratish va search_path¶
search_path β PG nomni qidiradigan schema'lar ro'yxati. SET search_path = shop dan keyin CREATE TABLE mahsulot aslida shop.mahsulot ni yaratadi va SELECT ... FROM mahsulot ham shop.mahsulot ni topadi β har safar shop. yozmasdan.
Schema afzalliklari dizayn nuqtai nazaridan:
- Tashkilot: bog'liq jadvallarni guruhlash (
billing.hisob,billing.tolov). - Ruxsat: schema darajasida
GRANTberish oson (bir butun modulga ruxsat). - Multi-tenancy: har ijarachiga alohida schema (19-bob β schema-per-tenant strategiyasi).
- Test izolyatsiyasi: bu kitobning har bobi o'z schema'sida ishlaydi (
ch09), bir-biriga xalal bermasin.
Ehtiyot:
search_pathni faqat seans (session) darajasidaSETqilsangiz, u keyingi ulanishda yo'qoladi. Doimiy qilish uchun foydalanuvchigaALTER ROLE ... SET search_path = ...yoki bazagaALTER DATABASE ...qo'llang. Skriptlarda esa har doim boshidaSET search_pathyozish odat tusiga kirsin.
5. To'liq misol: do'kon modelidan to'liq DDL ga (PG18'da ishga tushirilgan)¶
Endi hammasini birlashtiramiz. Quyidagi do'kon modeli oltita o'tkazish qoidasining hammasini qamraydi:
kategoriya,mijozβ mustaqil entity (Qoida 1).mahsulot1:Nkategoriya,buyurtma1:Nmijoz(Qoida 3).mijoz_profil1:1mijoz(Qoida 5).buyurtmaN:Mmahsulotorqalibuyurtma_satri(Qoida 4).mijoz_telefonβ kuchsiz entity (Qoida 6).
Quyidagi skript PostgreSQL 18.4 (port 5434) klasterida ch09 schema'sida HAQIQATAN ishga tushirildi. Har bir natija real psql chiqishi.
5.1 To'liq DDL skripti¶
CREATE SCHEMA IF NOT EXISTS ch09;
SET search_path = ch09;
-- 1) Mustaqil (ota) jadvallar AVVAL
CREATE TABLE kategoriya (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nomi text NOT NULL,
CONSTRAINT uq_kategoriya_nomi UNIQUE (nomi)
);
CREATE TABLE mijoz (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL,
ismi text NOT NULL,
yaratilgan timestamptz NOT NULL DEFAULT now(),
CONSTRAINT uq_mijoz_email UNIQUE (email)
);
-- 2) 1:N bola jadvallar (FK bola tomonda)
CREATE TABLE mahsulot (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
kategoriya_id bigint NOT NULL,
nomi text NOT NULL,
narx numeric(12,2) NOT NULL,
CONSTRAINT fk_mahsulot_kategoriya
FOREIGN KEY (kategoriya_id) REFERENCES kategoriya (id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT ck_mahsulot_narx_musbat CHECK (narx > 0)
);
CREATE TABLE buyurtma (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
mijoz_id bigint NOT NULL REFERENCES mijoz (id) ON DELETE RESTRICT,
holati text NOT NULL DEFAULT 'yangi',
yaratilgan timestamptz NOT NULL DEFAULT now(),
CONSTRAINT ck_buyurtma_holati
CHECK (holati IN ('yangi','tolangan','yetkazilgan','bekor'))
);
-- 3) 1:1 jadval (FK = PK, ixtiyoriy tomonda)
CREATE TABLE mijoz_profil (
mijoz_id bigint PRIMARY KEY REFERENCES mijoz (id) ON DELETE CASCADE,
bio text,
avatar_url text
);
-- 4) N:M junction jadval (kompozit PK)
CREATE TABLE buyurtma_satri (
buyurtma_id bigint NOT NULL REFERENCES buyurtma (id) ON DELETE CASCADE,
mahsulot_id bigint NOT NULL REFERENCES mahsulot (id) ON DELETE RESTRICT,
soni integer NOT NULL,
narx_snapshot numeric(12,2) NOT NULL,
PRIMARY KEY (buyurtma_id, mahsulot_id),
CONSTRAINT ck_satr_soni_musbat CHECK (soni > 0)
);
-- 5) Kuchsiz entity (kompozit PK: ega + qisman kalit)
CREATE TABLE mijoz_telefon (
mijoz_id bigint NOT NULL REFERENCES mijoz (id) ON DELETE CASCADE,
raqam text NOT NULL,
turi text NOT NULL DEFAULT 'asosiy',
PRIMARY KEY (mijoz_id, raqam)
);
-- 6) Indekslar: FK ustunlariga (PG FK ni avtomatik indekslamaydi)
CREATE INDEX ix_mahsulot_kategoriya_id ON mahsulot (kategoriya_id);
CREATE INDEX ix_buyurtma_mijoz_id ON buyurtma (mijoz_id);
CREATE INDEX ix_satr_mahsulot_id ON buyurtma_satri (mahsulot_id);
Ishga tushganda PG quyidagini chiqaradi (real, 5434 da):
CREATE SCHEMA
SET
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE INDEX
CREATE INDEX
CREATE INDEX
Yetti jadval va uchta indeks toza yaratildi.
5.2 Ma'lumot bilan to'ldirib, bog'lanishni tekshirish¶
SET search_path = ch09;
INSERT INTO kategoriya (nomi) VALUES ('Elektronika'), ('Kitoblar');
INSERT INTO mijoz (email, ismi) VALUES
('ali@example.com','Ali Valiyev'),
('lola@example.com','Lola Karimova');
INSERT INTO mijoz_profil (mijoz_id, bio) VALUES (1, 'Texnika ishqibozi');
INSERT INTO mijoz_telefon (mijoz_id, raqam, turi) VALUES
(1, '+998901112233', 'asosiy'),
(1, '+998935556677', 'ish');
INSERT INTO mahsulot (kategoriya_id, nomi, narx) VALUES
(1, 'Klaviatura', 250000.00),
(1, 'Sichqoncha', 120000.00),
(2, 'PostgreSQL kitobi', 90000.00);
INSERT INTO buyurtma (mijoz_id, holati) VALUES (1, 'yangi');
INSERT INTO buyurtma_satri (buyurtma_id, mahsulot_id, soni, narx_snapshot) VALUES
(1, 1, 2, 250000.00),
(1, 3, 1, 90000.00);
N:M bog'lanishni JOIN orqali o'qiymiz (buyurtma -> satr -> mahsulot):
SELECT b.id AS buyurtma, m.ismi AS mijoz, p.nomi AS mahsulot,
bs.soni, bs.narx_snapshot, bs.soni*bs.narx_snapshot AS satr_jami
FROM buyurtma b
JOIN mijoz m ON m.id = b.mijoz_id
JOIN buyurtma_satri bs ON bs.buyurtma_id = b.id
JOIN mahsulot p ON p.id = bs.mahsulot_id
ORDER BY p.nomi;
Natija (5434 da tekshirildi):
buyurtma | mijoz | mahsulot | soni | narx_snapshot | satr_jami
----------+-------------+-------------------+------+---------------+-----------
1 | Ali Valiyev | Klaviatura | 2 | 250000.00 | 500000.00
1 | Ali Valiyev | PostgreSQL kitobi | 1 | 90000.00 | 90000.00
(2 rows)
5.3 Constraint'lar haqiqatan ishlayotganini ko'rsatish¶
Sxema "qog'ozda" emas, baza majburlaydi. Buni uchta urinish bilan tasdiqlaymiz.
FK RESTRICT β tegishli mahsuloti bor kategoriyani o'chirib bo'lmaydi:
ERROR: update or delete on table "kategoriya" violates RESTRICT setting of
foreign key constraint "fk_mahsulot_kategoriya" on table "mahsulot"
DETAIL: Key (id)=(1) is referenced from table "mahsulot".
CHECK β narxi noldan katta bo'lishi shart:
ERROR: new row for relation "mahsulot" violates check constraint "ck_mahsulot_narx_musbat"
DETAIL: Failing row contains (4, 1, Tekin mahsulot, 0.00).
ON DELETE CASCADE β mijoz o'chirilsa, profili avtomatik o'chadi. (Test mijoz qo'shib, o'chirib, profil sonini solishtiramiz.)
INSERT INTO mijoz (email, ismi) VALUES ('test@x.com','Test');
INSERT INTO mijoz_profil (mijoz_id, bio)
VALUES (currval(pg_get_serial_sequence('mijoz','id')), 'vaqtinchalik');
SELECT count(*) AS profil_avval FROM mijoz_profil;
DELETE FROM mijoz WHERE email='test@x.com';
SELECT count(*) AS profil_keyin FROM mijoz_profil;
Natija (5434 da tekshirildi) β profil 2 dan 1 ga tushdi, ya'ni CASCADE ishladi:
5.4 \d bilan fizik tuzilishni tekshirish¶
psql ning \d <jadval> buyrug'i fizik sxemani ko'rsatadi. Bu yerda nomlash konvensiyasining ta'siri aniq ko'rinadi:
Real chiqish (5434 da):
Table "ch09.mahsulot"
Column | Type | Nullable | Default
---------------+---------------+----------+------------------------------
id | bigint | not null | generated always as identity
kategoriya_id | bigint | not null |
nomi | text | not null |
narx | numeric(12,2) | not null |
Indexes:
"mahsulot_pkey" PRIMARY KEY, btree (id)
"ix_mahsulot_kategoriya_id" btree (kategoriya_id)
Check constraints:
"ck_mahsulot_narx_musbat" CHECK (narx > 0::numeric)
Foreign-key constraints:
"fk_mahsulot_kategoriya" FOREIGN KEY (kategoriya_id) REFERENCES ch09.kategoriya(id) ON UPDATE CASCADE ON DELETE RESTRICT
Referenced by:
TABLE "ch09.buyurtma_satri" CONSTRAINT "buyurtma_satri_mahsulot_id_fkey" FOREIGN KEY (mahsulot_id) REFERENCES ch09.mahsulot(id) ON DELETE RESTRICT
Diqqat: o'zimiz nomlagan fk_mahsulot_kategoriya va ck_mahsulot_narx_musbat o'qiladi; junction jadvaldagi avtomatik buyurtma_satri_mahsulot_id_fkey esa β uzunroq, mashina yaratgan nom. Aynan shu farq nima uchun muhim constraint'larga o'zimiz nom berishimiz kerakligini ko'rsatadi.
5.5 Tozalash¶
Test schema'sini ortidan tozalaymiz:
CASCADE schema ichidagi barcha jadval/indeks/constraint'ni birga o'chiradi β test izolyatsiyasi uchun ideal.
6. Ekspert maslahatlari va ko'p uchraydigan xatolar¶
Tajriba bilan kelgan amaliy qoidalar:
| Xato / qaror | To'g'ri yondashuv |
|---|---|
| FK ustuniga indeks qo'ymaslik | Har FK ustuniga ix_ indeks qo'shing (JOIN va ota o'chirish tezligi). |
int ni PK qilish |
O'sib ketishi mumkin jadvallarda bigint (turni keyin o'zgartirish og'riqli). |
Pulni float/real da saqlash |
Har doim numeric(p,s) (yaxlitlash xatosi yo'q β 10-bob). |
timestamp (zonasiz) ishlatish |
timestamptz (vaqt mintaqasi bilan) β standart tanlov. |
| N:M ni ikki FK ustun bilan "to'g'ridan-to'g'ri" | Doim junction jadval; takrorni PRIMARY KEY/UNIQUE bilan to'sing. |
| Constraint'larni nomsiz qoldirish | Muhim constraint'larga fk_/uq_/ck_ nom bering (xato xabari + migratsiya). |
| Yaratish tartibini e'tiborsiz qoldirish | Avval ota, keyin bola; sirkulyar bo'lsa ALTER ... ADD CONSTRAINT. |
PostgreSQL 18+ yangiligi: taqsimlangan tizimga moslab, surrogate kalitni
uuidv7()bilan ham yaratish mumkin β bu vaqt bo'yicha tartiblanadigan UUID (019ec1f7-5c84-77f5-...), ya'ni B-tree indeksda UUIDv4 dagi tasodifiy tarqalishdan farqli, ketma-ket joylashadi. PG18 daid uuid PRIMARY KEY DEFAULT uuidv7()to'g'ridan-to'g'ri ishlaydi (5434 da sinab ko'rdim). Bu vaBIGINT IDENTITYo'rtasidagi trade-off 06-bobda batafsil.
Yakuniy fikr: logik modeldan fizik sxemaga o'tish β ijod emas, intizom. Oltita qoidani izchil qo'llang, izchil nomlang, tartibni hurmat qiling. Keyingi bobda esa eng nozik fizik dizayn qaroriga β to'g'ri ma'lumot turini tanlash ga β chuqur kirib boramiz.
Mashqlar¶
Oson¶
-
Entity -> jadval. "Kutubxona" domenida
kitobentity'si quyidagi atributlarga ega:isbn(majburiy, noyob),sarlavha(majburiy),nashr_yili(ixtiyoriy). Surrogateidbilan to'liqCREATE TABLEyozing, qaysi ustun(lar)gaNOT NULLvaUNIQUEkerakligini ko'rsating. -
1:N -> FK.
muallif1 β Nkitob(bir muallif ko'p kitob, har kitob bitta muallif). FK qaysi jadvalda bo'ladi va nomi qanday? FK ustuniniNOT NULLqilasizmi β qaror biznes qoidasiga qanday bog'liq? -
FK indeksi. Yangi
kitobjadvalidamuallif_idFK bor. PostgreSQL bu ustunga avtomatik indeks yaratadimi? Agar yo'q bo'lsa, kerakliCREATE INDEXni yozing va nima uchun kerakligini bir jumlada ayting. -
Nomlashni tuzating. Quyidagi nomlarni snake_case + bu bobdagi konvensiyaga keltiring: jadval
OrderItems, ustunCustomerID, FK constraint (mahsulotdan kategoriyaga) nomsiz, narx > 0 CHECK constraint nomsiz.
O'rta¶
-
N:M -> junction.
talabaN β Mkurs(bir talaba ko'p kursga yoziladi, bir kursda ko'p talaba). Bog'lovchi jadvalni yarating; ungaroyxatga_olingan(sana) bog'lanish atributini qo'shing; bir talaba bir kursga ikki marta yozilmasligini qanday kafolatlaysiz? -
1:1 qaror.
foydalanuvchiva uningpasport_malumoti(har foydalanuvchining 0 yoki 1 ta pasporti). FK qaysi jadvalga va nega? Ikki muqobil (FK=PK va alohidaid+UNIQUE FK) ni yozing va qaysi biri qachon afzalligini ayting. -
Kuchsiz entity. Onlayn-do'kon
buyurtmaningyetkazish_manzilihar buyurtmaga xos (kuchsiz entity, buyurtmasiz mavjud emas). To'liqCREATE TABLEyozing: kompozit PK qanday bo'ladi,ON DELETEstrategiyasi qanday va nega? -
DDL tartibi. Sizda quyidagi jadvallar bor:
buyurtma_satri,kategoriya,mahsulot,mijoz,buyurtma. UlarniCREATE TABLEqaysi tartibda yozasiz? Bog'liqlikni ko'rsatib, tartibni asoslang.
Qiyin¶
-
To'liq modeldan DDL. Quyidagi "blog" logik modelini to'liq PG DDL ga aylantiring:
muallif(1) β Nmaqola;maqolaN β Mteg(junction); harmaqolaga 0+izoh(1:N, kuchsiz emas β izohning o'zidsi bor);muallif1 β 1muallif_sozlama. Nomlash konvensiyasini, FK indekslarini, kamida ikkita CHECK ni va to'g'ri yaratish tartibini qo'shing. -
Nomlashni to'liq tuzating. Quyidagi sxema yomon nomlangan. To'liq qayta yozing (jadval, ustun, constraint, indeks nomlari):
Kamida 4 muammoni toping (nom, tur, indeks, constraint) va tuzating. -
Sirkulyar bog'lanish.
bolimjadvaliningboshliq_idustunixodimga ishora qiladi,xodimjadvaliningbolim_idustunibolimga ishora qiladi. Oddiy "avval ota, keyin bola" tartibi bu yerda nega yetmaydi? To'liq, ishlaydigan DDL skriptini yozing (jadvallarni yarating, keyin FK qo'shing). -
1:N ni N:M ga aylantirish (evolyutsiya). Boshida
mahsulot1 β Nkategoriyadeb modellashtirgansiz (mahsulot.kategoriya_id). Endi biznes o'zgardi: bir mahsulot bir nechta kategoriyada bo'lishi mumkin (N:M). Eski FK ustunidan junction jadvalga o'tish uchun DDL qadamlarini yozing: yangi junction jadvalni yarating, mavjud ma'lumotni unga ko'chiring, eski ustunni olib tashlang. -
Schema bilan tashkil etish. Bir bazada ikki modul bor:
shop(mahsulot, buyurtma) vaauth(foydalanuvchi, sessiya). Ikki schema yarating, har biriga bittadan jadval qo'ying,search_pathni to'g'ri sozlang vaauth.foydalanuvchigashop.buyurtmadan FK qo'ying (schema-aro FK). Bu ishlaydimi β sinab ko'ring (psql'da). -
Audit ustunlari va generated column.
buyurtma_satrijadvaliga: (a)yaratilgan timestamptzaudit ustunini; (b)jami numericnisoni * narx_snapshotdan STORED generated column sifatida qo'shing. Generated column uchun trigger keraksizligini asoslang (08-bobni eslang).
Yechimlar¶
Yechim β 1
isbn β noyob biznes kaliti, lekin barqarorlik uchun surrogate id ni PK qilamiz va isbn ga UNIQUE qo'yamiz:
CREATE TABLE kitob (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
isbn text NOT NULL,
sarlavha text NOT NULL,
nashr_yili integer, -- ixtiyoriy => NOT NULL yo'q
CONSTRAINT uq_kitob_isbn UNIQUE (isbn)
);
isbn va sarlavha β majburiy (NOT NULL); nashr_yili β ixtiyoriy (NULL ruxsat); isbn β noyob biznes kalit (UNIQUE), lekin PK emas (barqarorlik uchun surrogate afzal β 06-bob).
Yechim β 2
FK "ko'p" (N) tomonda, ya'ni kitob jadvalida, nomi muallif_id:
CREATE TABLE kitob (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
muallif_id bigint NOT NULL REFERENCES muallif (id),
sarlavha text NOT NULL
);
NOT NULL qarori biznes qoidasiga bog'liq: agar har kitob majburan bir muallifga tegishli bo'lsa (majburiy ishtirok) -> NOT NULL. Agar mualifsiz kitob (masalan, anonim/noma'lum) bo'lishi mumkin bo'lsa -> NULL ruxsat. Bu β modallik (04-bob) qarori.
Yechim β 3
Yo'q, PostgreSQL FK ustuniga avtomatik indeks yaratmaydi (faqat PK va UNIQUE'ga yaratadi). Shuning uchun:
Kerakli: muallif bo'yicha kitoblarni izlash (JOIN) tez bo'lsin, va muallif qatorini o'chirish/yangilashda PG butun kitob jadvalini skanerlamasin.
Yechim β 4
-- OrderItems -> buyurtma_satri (snake_case, birlik)
-- CustomerID -> mijoz_id (FK ustun nomi: ota_jadval + _id)
-- FK constraint -> fk_mahsulot_kategoriya
-- CHECK -> ck_mahsulot_narx_musbat
ALTER TABLE mahsulot
ADD CONSTRAINT fk_mahsulot_kategoriya
FOREIGN KEY (kategoriya_id) REFERENCES kategoriya (id);
ALTER TABLE mahsulot
ADD CONSTRAINT ck_mahsulot_narx_musbat CHECK (narx > 0);
Asosiy tuzatishlar: jadval nomi snake_case + birlik; FK ustuni <ota>_id; constraint'larga fk_/ck_ prefiksli aniq nom.
Yechim β 5
CREATE TABLE royxat (
talaba_id bigint NOT NULL REFERENCES talaba (id) ON DELETE CASCADE,
kurs_id bigint NOT NULL REFERENCES kurs (id) ON DELETE CASCADE,
royxatga_olingan date NOT NULL DEFAULT current_date,
PRIMARY KEY (talaba_id, kurs_id)
);
CREATE INDEX ix_royxat_kurs_id ON royxat (kurs_id);
Takrorni kompozit PRIMARY KEY (talaba_id, kurs_id) to'sadi β bir talaba bir kursga ikki marta yozilolmaydi. royxatga_olingan β bog'lanishning o'ziga xos atributi, tabiiy ravishda junction jadvalda. kurs_id ga indeks: PK ning birinchi ustuni talaba_id bo'lgani uchun, kurs bo'yicha qidiruvga alohida indeks kerak.
Yechim β 6
FK pasport_malumoti da (kamroq/ixtiyoriy mavjud tomonda), chunki har foydalanuvchining pasporti bo'lmasligi mumkin, lekin har pasport aynan bitta foydalanuvchiga tegishli.
-- Muqobil (a): FK = PK
CREATE TABLE pasport_malumoti (
foydalanuvchi_id bigint PRIMARY KEY REFERENCES foydalanuvchi (id) ON DELETE CASCADE,
seriya_raqam text NOT NULL
);
-- Muqobil (b): alohida id + UNIQUE FK
CREATE TABLE pasport_malumoti (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
foydalanuvchi_id bigint NOT NULL UNIQUE REFERENCES foydalanuvchi (id) ON DELETE CASCADE,
seriya_raqam text NOT NULL
);
(a) soddaroq va ortiqcha ustunsiz β odatda afzal. (b) faqat boshqa jadval bu jadvalga o'z id orqali ishora qilishi kerak bo'lganda yoki ORM talab qilganda. Ikkalasida ham 1:1 ni PRIMARY KEY/UNIQUE kafolatlaydi.
Yechim β 7
CREATE TABLE yetkazish_manzili (
buyurtma_id bigint NOT NULL REFERENCES buyurtma (id) ON DELETE CASCADE,
qator smallint NOT NULL, -- qisman kalit (bir buyurtmaga bir necha manzil bo'lsa)
shahar text NOT NULL,
kocha text NOT NULL,
PRIMARY KEY (buyurtma_id, qator)
);
Kompozit PK: (buyurtma_id, qator) β ega kaliti (buyurtma_id) + qisman kalit (qator). Agar har buyurtmaga aniq bitta manzil bo'lsa, qisman kalit shart emas β buyurtma_id ni o'zi PK bo'ladi (bu 1:1 ga aylanadi). ON DELETE CASCADE: buyurtma o'chsa, manzili ham o'chadi β chunki egasiz manzil ma'nosiz (kuchsiz entity belgisi).
Yechim β 8
Tartib bog'liqlik grafiga bo'ysunadi:
kategoriya,mijozβ hech kimga FK qo'ymaydi (ota).mahsulot(-> kategoriya),buyurtma(-> mijoz) β bitta otaga bog'liq.buyurtma_satri(-> buyurtma VA mahsulot) β oxirgi, ikkala otasi tayyor bo'lgach.
Asos: buyurtma_satri ikkala (buyurtma, mahsulot) ga FK qo'yadi, demak ular allaqachon mavjud bo'lishi shart. mahsulot kategoriya ga, buyurtma mijoz ga bog'liq. Mustaqillar birinchi.
Yechim β 9
-- 1) Ota jadvallar
CREATE TABLE muallif (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ismi text NOT NULL,
email text NOT NULL,
CONSTRAINT uq_muallif_email UNIQUE (email)
);
CREATE TABLE teg (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nomi text NOT NULL,
CONSTRAINT uq_teg_nomi UNIQUE (nomi)
);
-- 2) 1:N bola: maqola (muallifga)
CREATE TABLE maqola (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
muallif_id bigint NOT NULL REFERENCES muallif (id) ON DELETE RESTRICT,
sarlavha text NOT NULL,
korishlar integer NOT NULL DEFAULT 0,
CONSTRAINT ck_maqola_korishlar CHECK (korishlar >= 0)
);
-- 3) 1:1: muallif_sozlama (FK = PK)
CREATE TABLE muallif_sozlama (
muallif_id bigint PRIMARY KEY REFERENCES muallif (id) ON DELETE CASCADE,
til text NOT NULL DEFAULT 'uz'
);
-- 4) 1:N: izoh (maqolaga; o'z id si bor β kuchsiz emas)
CREATE TABLE izoh (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
maqola_id bigint NOT NULL REFERENCES maqola (id) ON DELETE CASCADE,
matn text NOT NULL,
baho smallint,
CONSTRAINT ck_izoh_baho CHECK (baho IS NULL OR baho BETWEEN 1 AND 5)
);
-- 5) N:M junction: maqola <-> teg
CREATE TABLE maqola_teg (
maqola_id bigint NOT NULL REFERENCES maqola (id) ON DELETE CASCADE,
teg_id bigint NOT NULL REFERENCES teg (id) ON DELETE CASCADE,
PRIMARY KEY (maqola_id, teg_id)
);
-- 6) FK indekslar
CREATE INDEX ix_maqola_muallif_id ON maqola (muallif_id);
CREATE INDEX ix_izoh_maqola_id ON izoh (maqola_id);
CREATE INDEX ix_maqola_teg_teg_id ON maqola_teg (teg_id);
Diqqat: izoh β kuchsiz entity EMAS (o'z id PK si bor, mustaqil identifikatsiya), lekin maqola ga ON DELETE CASCADE bilan bog'langan (maqola o'chsa izohlar ketadi). maqola_teg.maqola_id ga indeks kerak emas (kompozit PK birinchi ustuni), lekin teg_id ga kerak.
Yechim β 10
To'rt (va undan ko'p) muammo:
Products->mahsulot(snake_case, birlik).serial->bigint GENERATED ALWAYS AS IDENTITY(zamonaviy, standart, kattaroq diapazon).Price float->narx numeric(12,2)(pul hech qachonfloatda β yaxlitlash xatosi).- FK ustun
catId->kategoriya_id; FK constraint nomsiz ->fk_mahsulot_kategoriya. - FK ustuniga indeks yo'q ->
ix_qo'shildi.
CREATE TABLE mahsulot (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
kategoriya_id bigint NOT NULL,
narx numeric(12,2) NOT NULL,
CONSTRAINT fk_mahsulot_kategoriya
FOREIGN KEY (kategoriya_id) REFERENCES kategoriya (id),
CONSTRAINT ck_mahsulot_narx_musbat CHECK (narx > 0)
);
CREATE INDEX ix_mahsulot_kategoriya_id ON mahsulot (kategoriya_id);
Yechim β 11
"Avval ota, keyin bola" yetmaydi, chunki bog'liqlik ikki tomonlama (sirkulyar): bolim ni yaratish uchun xodim kerak (boshliq_id), lekin xodim ni yaratish uchun bolim kerak (bolim_id). Hech biri "birinchi" bo'lolmaydi.
Yechim β avval FK'siz yarat, keyin ALTER bilan qo'sh:
CREATE TABLE bolim (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nomi text NOT NULL,
boshliq_id bigint -- FK keyin qo'shiladi
);
CREATE TABLE xodim (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ismi text NOT NULL,
bolim_id bigint NOT NULL REFERENCES bolim (id)
);
ALTER TABLE bolim
ADD CONSTRAINT fk_bolim_boshliq
FOREIGN KEY (boshliq_id) REFERENCES xodim (id);
CREATE INDEX ix_xodim_bolim_id ON xodim (bolim_id);
CREATE INDEX ix_bolim_boshliq_id ON bolim (boshliq_id);
boshliq_id ni NULL ruxsat qoldirdik β chunki bo'lim yaratilganda boshlig'i hali tayinlanmagan bo'lishi mumkin (tovuq-tuxum muammosini yumshatadi). Muqobil: DEFERRABLE INITIALLY DEFERRED constraint (11-bob).
Yechim β 12
-- 1) Yangi junction jadval
CREATE TABLE mahsulot_kategoriya (
mahsulot_id bigint NOT NULL REFERENCES mahsulot (id) ON DELETE CASCADE,
kategoriya_id bigint NOT NULL REFERENCES kategoriya (id) ON DELETE CASCADE,
PRIMARY KEY (mahsulot_id, kategoriya_id)
);
CREATE INDEX ix_mahkat_kategoriya_id ON mahsulot_kategoriya (kategoriya_id);
-- 2) Mavjud ma'lumotni ko'chir (eski 1:N bog'lanishlarni junction'ga)
INSERT INTO mahsulot_kategoriya (mahsulot_id, kategoriya_id)
SELECT id, kategoriya_id FROM mahsulot WHERE kategoriya_id IS NOT NULL;
-- 3) Eski FK ustunni olib tashla
ALTER TABLE mahsulot DROP COLUMN kategoriya_id;
Tartib muhim: avval junction tayyor, keyin ma'lumotni ko'chir (yo'qotmaslik uchun), keyingina eski ustunni o'chir. Bu β sxema evolyutsiyasining tipik namunasi (23-bob: expand-contract).
Yechim β 13
CREATE SCHEMA IF NOT EXISTS auth;
CREATE SCHEMA IF NOT EXISTS shop;
CREATE TABLE auth.foydalanuvchi (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE
);
-- schema-aro FK: shop.buyurtma -> auth.foydalanuvchi
CREATE TABLE shop.buyurtma (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
foydalanuvchi_id bigint NOT NULL REFERENCES auth.foydalanuvchi (id),
yaratilgan timestamptz NOT NULL DEFAULT now()
);
SET search_path = shop, auth, public; -- ikkala schema ham qidiriladi
CREATE INDEX ix_buyurtma_foydalanuvchi_id ON shop.buyurtma (foydalanuvchi_id);
Ha, schema-aro FK ishlaydi β FK schema chegarasini kesib o'tishi mumkin (baza bitta bo'lsa), faqat to'liq nomni (auth.foydalanuvchi) ko'rsatish kerak. search_path = shop, auth, public ikkala modulning jadvallarini prefiks'siz topishga imkon beradi.
Yechim β 14
ALTER TABLE buyurtma_satri
ADD COLUMN yaratilgan timestamptz NOT NULL DEFAULT now();
ALTER TABLE buyurtma_satri
ADD COLUMN jami numeric(14,2)
GENERATED ALWAYS AS (soni * narx_snapshot) STORED;
Generated column uchun trigger keraksiz, chunki jami faqat o'sha qatorning ustunlaridan (soni, narx_snapshot) hisoblanadi β boshqa jadval ma'lumotiga bog'liq emas. PostgreSQL STORED generated column'ni har INSERT/UPDATE da avtomatik, har doim izchil hisoblaydi; drift mumkin emas (08-bob). Trigger faqat hosil qiymat boshqa jadval agregatiga (masalan, count/sum) bog'liq bo'lganda kerak edi.
β¬ οΈ Oldingi: 08 β Normalizatsiya II: BCNF, 4NF, 5NF va denormalizatsiya Β· π README Β· Keyingi: 10 β To'g'ri ma'lumot turini tanlash (PostgreSQL boy turlari) β‘οΈ