Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. Ustunlar bilan ishlash
- 2.1 ADD COLUMN
- 2.2 RENAME, DROP, NOT NULL, DEFAULT
- 2.3 Nom o'zgartirish va o'chirish — ehtiyot bo'ling
- 3. Ustun turini o'zgartirish
- 3.1 ALTER TYPE va USING
- 3.2 Tur o'zgarishi narxi
- 4. Cheklov qo'shish
- 4.1 Mavjud ma'lumotga zid cheklov
- 4.2 NOT VALID va VALIDATE
- 5. ALTER va qulflar
- 5.1 ACCESS EXCLUSIVE
- 5.2 Navbat tuzog'i va lock_timeout
- 5.3 Qaysi ALTER tez, qaysi biri sekin
- 6. SQL qadami: holat 21 — ustuvorlik, muddat, kichik vazifalar
- 6.1 004-muddat-ustuvorlik.sql
- 6.2 005-namuna-muddat.sql
- 6.3 Tekshiruv
- 6.4 Commit
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): tez yoki sekin?
- 2-mashq (o'rta): ixtiyoriy telefon
- 3-mashq (qiyin): enum'dan songa
- 4-mashq: SQL qadami
- 9. Real ishda
- Xulosa
- Manbalar
PostgreSQL ALTER TABLE: ustun qo'shish, turini o'zgartirish va qulflar
Qisqacha:
ALTER TABLEishlab turgan jadval tuzilmasini o'zgartiradi: ustun qo'shadi (ADD COLUMN), o'chiradi, nomini va turini o'zgartiradi (ALTER COLUMN … TYPE … USING), cheklov qo'shadi. Mavjud qatorlarga zid o'zgarish (NOT NULLustunDEFAULTsiz, buziladiganCHECK) rad etiladi. Ko'pALTERjadvalga eng kuchli qulfni —ACCESS EXCLUSIVEni oladi: tugaguncha jadvaldan o'qish ham to'xtaydi. Shuning uchun katta jadvalda tez buyruqlarni tanlaysiz (DEFAULTliADD COLUMN,NOT VALID) valock_timeoutqo'yasiz.
Bu darsda
ADD,DROP,RENAME COLUMN,SET/DROP NOT NULL,SET DEFAULTni yozasiz.ALTER COLUMN … TYPE … USINGbilan ustun turini ma'lumoti bilan birga o'zgartirasiz.- Mavjud ma'lumotga cheklov qo'shasiz,
NOT VALIDvaVALIDATEfarqini bilasiz. ALTERqaysi qulfni olishini, qaysi buyruq tez va qaysi biri jadvalni qayta yozishini tushunasiz.vazifalarga ustuvorlik (enum), muddat, bajarilgan vaqt va kichik vazifalar qo'shib, holat 21 ni commit qilasiz.
Oldin bilishingiz kerak: CREATE TABLE va cheklovlar, Foreign key va referential actions, Agregatlar, GROUP BY va HAVING, PostgreSQL ma'lumot turlari.
1. Nega bu kerak?
22-qismdagi frontend vazifalar ilovasida vazifaning faqat uchta maydoni bor edi: id, matn, bajarildi. Endi foydalanuvchilar so'rayapti:
- "Muhim vazifalarni ajratib qo'yay" — ustuvorlik.
- "Ertaga soat 18:00 gacha" — muddat.
- "Qachon bajardim?" — bajarilgan vaqt.
- "Kurs ishini kichik qadamlarga bo'lay" — kichik vazifalar.
Jadvalni o'chirib, qaytadan yaratsak-chi? Unda 40 ta vazifa yo'qoladi. Haqiqiy ilovada esa bu — millionlab foydalanuvchi ma'lumoti. Jadval ishlab turgan holda, ma'lumotni saqlab o'zgarishi kerak. Buni ALTER TABLE qiladi.
Lekin bitta muhim gap. ALTER TABLE paytida baza jadvalni "qulflab" qo'yadi. Kichik jadvalda bu bir millisekund. 2 million qatorli jadvalda esa noto'g'ri tanlangan buyruq jadvalni bir necha soniya — yoki daqiqa — band qiladi. Shu vaqtda ilovaning hamma so'rovlari navbatda turadi va sayt "qotib qoladi".
O'xshatish: ishlab turgan do'konni ta'mirlash. Javonga yangi narx yorlig'ini yopishtirish — bir zumda, xaridorlar sezmaydi. Butun javonni almashtirish uchun esa do'konni yopishga to'g'ri keladi. Usta qaysi ish qaysi turga kirishini oldindan bilishi kerak.
2. Ustunlar bilan ishlash
2.1 ADD COLUMN
Bu bo'limdagi tajribalar holat 15 bazasida, BEGIN … ROLLBACK ichida. PostgreSQL'da ALTER TABLE ham tranzaksiyali — ROLLBACK uni ham qaytaradi (SQL tili va buyruq turlari darsida DROP TABLE ni shunday qaytargan edik).
Oddiy ustun qo'shish: ALTER TABLE vazifalar ADD COLUMN izoh text;. Mavjud 40 qatorda yangi ustun NULL bo'ladi.
Endi majburiy ustun qo'shib ko'ramiz. Kanon (PostgreSQL 18.6):
ALTER TABLE vazifalar ADD COLUMN ustuvorlik text NOT NULL;Natija:
ERROR: column "ustuvorlik" of relation "vazifalar" contains null valuesTarjimasi: "vazifalar jadvalidagi ustuvorlik ustunida null qiymatlar bor". Jadvalda 40 qator bor, yangi ustunga qiymat yo'q — ular NULL bo'lardi, NOT NULL esa ruxsat bermaydi. Yechim — DEFAULT: mavjud qatorlar shu qiymatni oladi. Biz ishga tushirdik (PostgreSQL 17.6, Windows 11):
BEGIN;
ALTER TABLE vazifalar
ADD COLUMN ustuvorlik text NOT NULL DEFAULT 'orta';
SELECT ustuvorlik, count(*) FROM vazifalar GROUP BY 1;
ROLLBACK;Natija:
BEGIN
ALTER TABLE
ustuvorlik | count
------------+-------
orta | 40
(1 row)
ROLLBACKHamma 40 vazifa 'orta' ni oldi. IF NOT EXISTS qo'shsangiz (ADD COLUMN IF NOT EXISTS …), ustun bor bo'lsa xato o'rniga NOTICE chiqadi va buyruq o'tkaziladi.
2.2 RENAME, DROP, NOT NULL, DEFAULT
Kanon (PostgreSQL 18.6) — nom va NOT NULL ni o'zgartirib, qaytarish:
BEGIN;
ALTER TABLE vazifalar RENAME COLUMN matn TO sarlavha;
ALTER TABLE vazifalar RENAME COLUMN sarlavha TO matn;
ALTER TABLE vazifalar ALTER COLUMN bajarildi DROP NOT NULL;
ALTER TABLE vazifalar ALTER COLUMN bajarildi SET NOT NULL;
ROLLBACK;Natija:
BEGIN
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ROLLBACKQisqa ma'lumotnoma:
| Buyruq | Nima qiladi |
|---|---|
RENAME COLUMN a TO b |
Ustun nomi |
RENAME TO yangi_nom |
Jadval nomi |
DROP COLUMN a |
Ustunni ma'lumoti bilan o'chiradi |
ALTER COLUMN a SET NOT NULL |
Majburiy qiladi (hamma qator tekshiriladi) |
ALTER COLUMN a DROP NOT NULL |
Ixtiyoriy qiladi |
ALTER COLUMN a SET DEFAULT x |
Yangi qatorlar uchun sukut |
DROP CONSTRAINT nom |
Cheklovni olib tashlaydi |
SET DEFAULT faqat yangi qatorlarga ta'sir qiladi, mavjudlarini o'zgartirmaydi. Biz ishga tushirdik (PostgreSQL 17.6, ROLLBACK bilan) — ALTER COLUMN bajarildi SET DEFAULT true dan keyin yangi vazifa bajarildi = t bilan yozildi, eski 40 tasi o'zgarmadi.
2.3 Nom o'zgartirish va o'chirish — ehtiyot bo'ling
RENAME bazada bir zumda bajariladi. Lekin uni ishlatadigan hamma kod — ilova, hisobotlar, boshqa jamoalarning skriptlari — darhol buziladi. Biz ishga tushirdik (PostgreSQL 17.6):
BEGIN;
ALTER TABLE vazifalar RENAME TO ishlar;
SELECT count(*) FROM vazifalar;
ROLLBACK;Natija:
BEGIN
ALTER TABLE
ERROR: relation "vazifalar" does not exist
LINE 1: SELECT count(*) FROM vazifalar;
^
ROLLBACKIshlab turgan tizimda nom o'zgartirish bir necha bosqichda qilinadi: yangi ustun qo'shiladi, ilova ikkalasiga yozadi, ma'lumot ko'chiriladi, ilova yangisidan o'qiydi, eskisi oxirida o'chiriladi. Bu expand–contract (kengaytirib-qisqartirish) naqshi. Uni migratsiya vositalari bilan 27-qismda ko'ramiz.
DROP COLUMN ham bir zumda — PostgreSQL ustunni "ko'rinmas" deb belgilaydi, diskdan esa keyin tozalaydi. Lekin ma'lumot qaytmaydi. Yana bir nozik joy: biz ishga tushirdik (PostgreSQL 17.6) — ALTER TABLE vazifalar DROP COLUMN egasi_id hech qanday ogohlantirishsiz bajarildi. U bilan birga vazifalar_egasi_id_fkey tashqi kaliti va vazifalar_egasi_id_idx indeksi ham jim o'chdi. (Biz ROLLBACK qildik.) Ustunga bog'liq ko'rinish (view) yoki boshqa jadvalning tashqi kaliti bo'lsa esa xato chiqadi va CASCADE talab qilinadi.
Tekshirib ko'ring:
ALTER TABLE foydalanuvchilar ADD COLUMN telefon text NOT NULL DEFAULT ''— yaxshi fikrmi?
Javob
Xato bermaydi, lekin yomon. "Telefon yo'q" ma'nosi '' bilan ifodalanadi — NULL mantiqi darsida aytganimizdek, NULL aynan shu uchun bor. Ixtiyoriy maydon — NOT NULL siz, DEFAULT siz: ADD COLUMN telefon text.
3. Ustun turini o'zgartirish
3.1 ALTER TYPE va USING
ALTER COLUMN ustun TYPE yangi_tur — mavjud qiymatlarni yangi turga aylantiradi. Ba'zi aylantirishlar avtomatik. Kanon (PostgreSQL 18.6):
BEGIN;
ALTER TABLE vazifalar ALTER COLUMN bajarildi TYPE text;
SELECT DISTINCT bajarildi FROM vazifalar;
ALTER TABLE vazifalar ALTER COLUMN bajarildi TYPE boolean;
ROLLBACK;Natija:
BEGIN
ALTER TABLE
bajarildi
-----------
false
true
(2 rows)
ERROR: column "bajarildi" cannot be cast automatically to type boolean
HINT: You might need to specify "USING bajarildi::boolean".
ROLLBACKboolean → text avtomatik: 'true'/'false'. Teskarisi esa yo'q. Tarjimasi: "bajarildi ustunini boolean turiga avtomatik o'girib bo'lmaydi. Maslahat: USING bajarildi::boolean yozish kerak bo'lishi mumkin".
USING ifoda — "har qatorning yangi qiymatini shu ifoda bilan hisobla". Masalan boolean ni matnli holatga aylantirish. Kanon (PostgreSQL 18.6) — yana BEGIN … ROLLBACK ichida, bazangiz o'zgarmasin:
BEGIN;
ALTER TABLE vazifalar
ALTER COLUMN bajarildi DROP DEFAULT,
ALTER COLUMN bajarildi TYPE text USING
CASE WHEN bajarildi THEN 'bajarilgan' ELSE 'faol' END;
SELECT bajarildi, count(*) FROM vazifalar
GROUP BY 1 ORDER BY 1;
ROLLBACK;Natija:
BEGIN
ALTER TABLE
bajarildi | count
------------+-------
bajarilgan | 20
faol | 20
(2 rows)
ROLLBACKIkki narsaga qarang:
- Bitta
ALTER TABLEda bir nechta amal — vergul bilan. Hammasi bitta buyruq: jadval bir marta qulflanadi va bir marta o'qiladi. DROP DEFAULTavval keldi. Eski sukutfalse(boolean) — yangitextturiga to'g'ri kelmasligi mumkin. Avval uni olib tashlab, kerak bo'lsa keyin yangisini qo'yasiz.
CASE WHEN … THEN … ELSE … END — SQL'dagi "agar-aks holda" ifodasi. Uni keyingi darsda — Ifodalar va o'rnatilgan funksiyalar — batafsil o'rganamiz.
3.2 Tur o'zgarishi narxi
Ko'p tur o'zgarishlari butun jadvalni qayta yozadi: har qator yangi turda yangidan yoziladi, indekslar qayta quriladi. Shu vaqt davomida jadval qulflangan. Istisnolar bor — masalan varchar(50) → varchar(100) yoki text — faqat metadata o'zgaradi. Bu ro'yxatni PostgreSQL hujjatining ALTER TABLE sahifasidan tekshiring.
4. Cheklov qo'shish
4.1 Mavjud ma'lumotga zid cheklov
ADD CONSTRAINT yangi cheklovni hamma mavjud qatorlarda tekshiradi. Foreign key darsida yetimlar ALTER ni yiqitganini ko'rgan edik. CHECK bilan ham shunday. Kanon (PostgreSQL 18.6) — "matn 30 belgidan oshmasin":
ALTER TABLE vazifalar
ADD CONSTRAINT qisqa CHECK (char_length(matn) <= 30);Natija:
ERROR: check constraint "qisqa" of relation "vazifalar" is violated by some rowTarjimasi: "qisqa tekshiruv cheklovi ba'zi qatorlar tomonidan buzilgan". Uzun vazifalar bor (eng uzuni 48 belgi — Agregatlar darsida o'lchagan edik). Qaysilari — xabarda yo'q; ularni SELECT … WHERE char_length(matn) > 30 bilan topasiz.
4.2 NOT VALID va VALIDATE
Katta jadvalda hamma qatorni tekshirish uzoq va bu vaqtda jadval qulflangan. Ikki bosqichli yo'l bor:
ADD CONSTRAINT … NOT VALID— cheklov qo'shiladi, lekin eski qatorlar tekshirilmaydi. YangiINSERT/UPDATElar darhol tekshiriladi. Bu bir zumda.VALIDATE CONSTRAINT …— eski qatorlarni tekshiradi. Bu uzoq, lekin kuchsizroq qulf oladi — o'qish va yozish davom etadi.
Kanon (PostgreSQL 18.6):
BEGIN;
ALTER TABLE vazifalar
ADD CONSTRAINT qisqa CHECK (char_length(matn) <= 30) NOT VALID;
ALTER TABLE vazifalar VALIDATE CONSTRAINT qisqa;
ROLLBACK;Natija:
BEGIN
ALTER TABLE
ERROR: check constraint "qisqa" of relation "vazifalar" is violated by some row
ROLLBACKNOT VALID o'tdi. VALIDATE esa eski uzun matnlarni topdi — xuddi oddiy ADD CONSTRAINT dagidek. NOT VALID buzilgan ma'lumotni "sehrli" tuzatmaydi: u faqat qachon tekshirishni tanlash imkonini beradi. Haqiqiy tartib: NOT VALID → eski ma'lumotni tuzatish (UPDATE) → VALIDATE.
Tekshirib ko'ring:
NOT VALIDbilan qo'shilganCHECKbor jadvalda eski, qoidaga zid qatorniUPDATEqilsangiz (boshqa ustunini) nima bo'ladi?
Javob
Xato. UPDATE qatorning yangi nusxasini yozadi va yangi nusxa cheklov bilan tekshiriladi. Ya'ni eski "yomon" qator birinchi tahrirdayoq qoidaga moslashtirilishi kerak bo'ladi. Shuning uchun NOT VALID ni uzoq qoldirmang — ma'lumotni tuzatib, tezroq VALIDATE qiling.
5. ALTER va qulflar
5.1 ACCESS EXCLUSIVE
Qulf (lock) — "bu jadval bilan hozir men ishlayapman, kutib turing" degan belgi. PostgreSQL'da qulfning bir necha darajasi bor. Eng kuchlisi — ACCESS EXCLUSIVE: u boshqa hamma amalni, hatto oddiy SELECT ni ham to'xtatadi. Ko'p ALTER TABLE buyruqlari aynan shuni oladi. Kanon (PostgreSQL 18.6):
BEGIN;
ALTER TABLE vazifalar ADD COLUMN izoh text;
SELECT relation::regclass, mode, granted
FROM pg_locks WHERE relation = 'vazifalar'::regclass;
ROLLBACK;Natija:
BEGIN
ALTER TABLE
relation | mode | granted
-----------+---------------------+---------
vazifalar | AccessExclusiveLock | t
(1 row)
ROLLBACKpg_locks — hozir olingan qulflar ro'yxati (tizim ko'rinishi). Qulf tranzaksiya oxirigacha turadi — ALTER tugagan bo'lsa ham, COMMIT/ROLLBACK gacha. Qulflar haqida to'liq — Qulflar: row, table va advisory.
5.2 Navbat tuzog'i va lock_timeout
Haqiqiy xavf — ALTER ning o'zi emas, navbat. Tasavvur qiling: jadvalda uzoq hisobot so'rovi ishlayapti. Siz ALTER yuborasiz. U hisobot tugashini kutadi. Shu orada kelgan har yangi SELECT esa endi ALTER ning orqasida navbatga turadi. Bitta tez ALTER butun saytni to'xtatishi mumkin.
sequenceDiagram
participant H as Hisobot so'rovi
participant A as ALTER TABLE
participant S as Ilova SELECT'lari
H->>H: jadvalni o'qiyapti (uzoq)
A-->>H: ACCESS EXCLUSIVE kerak — kutadi
S-->>A: o'qish kerak — ALTER ortidan kutadi
H->>H: tugadi
A->>A: bajariladi
A->>S: endi o'qish mumkinDiagrammaning o'rtasiga qarang: S hisobot bilan emas, ALTER bilan to'qnashdi. Himoya — lock_timeout: "qulfni shuncha vaqtda ololmasang — voz kech". Biz buni ikki terminalda sinadik (PostgreSQL 17.6). Ikki psql oynasi — ikki alohida sessiya (bazaga alohida ulanish). Vaqt bo'yicha:
| Qadam | A sessiya | B sessiya | Natija |
|---|---|---|---|
| 1 | BEGIN; |
— | Tranzaksiya ochildi |
| 2 | ALTER TABLE vazifalar ADD COLUMN izoh text; |
— | A ACCESS EXCLUSIVE qulfini oldi |
| 3 | (4 soniya kutadi, COMMIT yo'q) |
SET lock_timeout = '1s'; |
— |
| 4 | — | SELECT count(*) FROM vazifalar; |
B qulfni kutadi |
| 5 | — | — | 1 soniyadan keyin B xato oladi |
| 6 | ROLLBACK; |
— | Qulf bo'shadi |
B sessiyadagi buyruqlar (ikki sessiya kerakligi uchun bu blokni bitta oynada takrorlab bo'lmaydi):
SET lock_timeout = '1s';
SELECT count(*) FROM vazifalar;Natija:
SET
ERROR: canceling statement due to lock timeout
LINE 1: SELECT count(*) FROM vazifalar;
^Tarjimasi: "qulf kutish vaqti tugagani uchun buyruq bekor qilindi". Bu yerda SELECT kutishdan voz kechdi. Amalda esa lock_timeout ni ALTER dan oldin qo'yasiz: ALTER qulfni tez ololmasa, u o'zi voz kechadi va ilova so'rovlari navbatda qolmaydi. Keyin ALTER ni qayta urinasiz.
5.3 Qaysi ALTER tez, qaysi biri sekin
Kanon buni 1 million qatorli jadvalda o'lchagan (PostgreSQL 18.6, Windows 11 + Docker WSL2, \timing, bitta o'lchov):
- ADD COLUMN … DEFAULT 'faol'faqat metadata4,5 ms
- ADD COLUMN … DEFAULT random()jadval qayta yozildi467 ms
- ALTER COLUMN id TYPE bigintjadval qayta yozildi512 ms
Manba: Kanon o'lchovi: PostgreSQL 18.6, 1 000 000 qatorli vaqtinchalik jadval (66 MB), \timing: 4.546, 467.001, 511.990 ms, 2026-10-24
Birinchi ustun deyarli ko'rinmaydi — 100 barobardan ko'p farq. Sababi:
- Doimiy
DEFAULT('faol',0,false) — PostgreSQL 11 dan beri qiymat jadval metama'lumotiga yoziladi, qatorlar o'zgarmaydi. Har qatorga bir xil qiymat — saqlash shart emas. - O'zgaruvchan
DEFAULT(random(),clock_timestamp()) — har qatorga boshqa qiymat kerak, jadval qayta yoziladi. - Tur o'zgarishi (
int→bigint) — har qator yangi formatda qayta yoziladi.
66 MB da yarim soniya. 30 GB jadvalda — daqiqalar, va shuncha vaqt jadval qulflangan. Katta jadvalda tur o'zgarishi uchun boshqa yo'llar bor (yangi ustun, bosqichma-bosqich ko'chirish). Bu — 27-qism va kasb darslarining mavzusi.
Diqqat: Yuqoridagi raqamlar kanon mashinasida, bitta o'lchov. Sizda boshqacha bo'ladi. Muhimi — tartibi: metadata o'zgarishi millisekund, qayta yozish — jadval hajmiga proporsional.
6. SQL qadami: holat 21 — ustuvorlik, muddat, kichik vazifalar
6.1 004-muddat-ustuvorlik.sql
Endi o'rganganlarimizni kanon migratsiyasida ko'ramiz. Fayl to'liq (izohlar telefonga qisqartirildi, kod aynan):
CREATE TYPE ustuvorlik AS ENUM ('past', 'orta', 'yuqori');
ALTER TABLE vazifalar
ADD COLUMN ustuvorlik ustuvorlik NOT NULL DEFAULT 'orta',
ADD COLUMN muddat timestamptz, -- NULL = muddatsiz
ADD COLUMN bajarilgan_vaqt timestamptz,
ADD COLUMN ota_id bigint; -- kichik vazifa
-- Mavjud bajarilganlar qachon bajarilgani NOMA'LUM:
-- eng erta mumkin bo'lgan qiymat — yaratilgan vaqt
UPDATE vazifalar SET bajarilgan_vaqt = yaratilgan_vaqt
WHERE bajarildi;
ALTER TABLE vazifalar
-- bajarildi = true <=> bajarilgan_vaqt bor
ADD CONSTRAINT vazifalar_bajarilgan_vaqt_check
CHECK (bajarildi = (bajarilgan_vaqt IS NOT NULL)),
ADD CONSTRAINT vazifalar_ota_id_check CHECK (ota_id <> id),
-- kichik vazifa faqat O'SHA egasining vazifasiga
ADD CONSTRAINT vazifalar_id_egasi_id_key UNIQUE (id, egasi_id),
ADD CONSTRAINT vazifalar_ota_id_fkey FOREIGN KEY (ota_id, egasi_id)
REFERENCES vazifalar (id, egasi_id) ON DELETE CASCADE;
CREATE INDEX vazifalar_ota_id_idx ON vazifalar (ota_id)
WHERE ota_id IS NOT NULL;Qatorma-qator nima bo'ldi:
| Qism | Nega aynan shunday |
|---|---|
CREATE TYPE … AS ENUM |
Ustuvorlik — faqat 3 qiymat, e'lon tartibida saralanadi |
NOT NULL DEFAULT 'orta' |
Doimiy sukut — tez, 40 vazifa "o'rta" bo'ldi |
muddat, bajarilgan_vaqt, ota_id — NULL li |
NULL ning bitta aniq ma'nosi bor (NULL mantiqi) |
UPDATE … WHERE bajarildi |
CHECK dan oldin — aks holda 20 ta bajarilgan qator uni buzardi |
CHECK (bajarildi = (bajarilgan_vaqt IS NOT NULL)) |
Ikki ustun bir-biriga zid bo'lmasin |
CHECK (ota_id <> id) |
Vazifa o'ziga o'zi ota bo'lmasin |
Tarkibiy tashqi kalit (ota_id, egasi_id) |
Kichik vazifa faqat o'sha egasining vazifasiga ulanadi |
Partial indeks WHERE ota_id IS NOT NULL |
Ko'p vazifaning otasi yo'q — indeks faqat kerakli qatorlarga |
Tarkibiy tashqi kalitni batafsilroq ko'ramiz. Oddiy REFERENCES vazifalar (id) bo'lsa, Aziz o'z vazifasini Dilnozaning vazifasiga "kichik vazifa" qilib ulay olardi. (ota_id, egasi_id) → (id, egasi_id) esa "ota vazifa ham aynan shu egaga tegishli bo'lsin" deydi. Buning uchun ota tomonda UNIQUE (id, egasi_id) kerak (Foreign key: ota ustunlari noyob bo'lishi shart). id o'zi noyob, shuning uchun bu juftlik ham noyob — mantiqan ortiqcha, lekin tashqi kalit uni talab qiladi.
Bu qarorning narxi bor: kanon 2 mln qatorli bazada shu UNIQUE indeks qo'shimcha 111 MB egallashini qayd etgan. Kanon uni ongli ravishda qoldirgan — egalik kafolati bazada bo'lsin. Bu qarorni Amaliyot: real loyiha sxemasi darsida yana muhokama qilamiz.
ON DELETE CASCADE — ota vazifa o'chirilsa, kichik vazifalari ham o'chadi. Enum'lar haqida batafsil (qiymat qo'shish, o'chirish cheklovlari) — Pul, miqdor va enum'larni modellash. Partial indeks — Partial, expression va covering indekslar.
6.2 005-namuna-muddat.sql
Namuna fayl uch ish qiladi. 20 ta bajarilgan vazifaga aniq bajarilgan vaqt yozadi (yaratilgan_vaqt + interval). 21 ta vazifaga ustuvorlik va muddat beradi (16 tasi muddatli). Va 7 ta kichik vazifani ikki darajada qo'shadi ("Kurs ishi" → "Jadvallarni loyihalash" → "ER diagramma chizish"). Fayl UPDATE … FROM (VALUES …) ishlatadi — UPDATE va DELETE xavfsiz dagi UPDATE … FROM ning o'zi, faqat ikkinchi jadval o'rnida VALUES ro'yxati.
Ikkala faylni qo'llang:
U=postgres://postgres@127.0.0.1:5499/vazifalar
for f in sql/004-muddat-ustuvorlik.sql sql/005-namuna-muddat.sql
do
psql -X -1 -v ON_ERROR_STOP=1 -d $U -f $f || break
done6.3 Tekshiruv
Kanon (holat 21, PostgreSQL 18.6) — \d vazifalar. Avval pastdagi to'rt yangi ustunga (ustuvorlik … ota_id), keyin Check constraints va Foreign-key constraints bo'limlariga qarang:
Table "public.vazifalar"
Column | Type | Collation | Nullable | Default
-----------------+--------------------------+-----------+----------+------------------------------
id | bigint | | not null | generated always as identity
egasi_id | bigint | | not null |
matn | text | | not null |
bajarildi | boolean | | not null | false
yaratilgan_vaqt | timestamp with time zone | | not null | now()
ustuvorlik | ustuvorlik | | not null | 'orta'::ustuvorlik
muddat | timestamp with time zone | | |
bajarilgan_vaqt | timestamp with time zone | | |
ota_id | bigint | | |
Indexes:
"vazifalar_pkey" PRIMARY KEY, btree (id)
"vazifalar_egasi_id_idx" btree (egasi_id)
"vazifalar_id_egasi_id_key" UNIQUE CONSTRAINT, btree (id, egasi_id)
"vazifalar_ota_id_idx" btree (ota_id) WHERE ota_id IS NOT NULL
Check constraints:
"vazifalar_bajarilgan_vaqt_check" CHECK (bajarildi = (bajarilgan_vaqt IS NOT NULL))
"vazifalar_matn_check" CHECK (matn = btrim(matn) AND char_length(matn) >= 1 AND char_length(matn) <= 100)
"vazifalar_ota_id_check" CHECK (ota_id <> id)
Foreign-key constraints:
"vazifalar_egasi_id_fkey" FOREIGN KEY (egasi_id) REFERENCES foydalanuvchilar(id) ON DELETE CASCADE
"vazifalar_ota_id_fkey" FOREIGN KEY (ota_id, egasi_id) REFERENCES vazifalar(id, egasi_id) ON DELETE CASCADE
Referenced by:
TABLE "vazifalar" CONSTRAINT "vazifalar_ota_id_fkey" FOREIGN KEY (ota_id, egasi_id) REFERENCES vazifalar(id, egasi_id) ON DELETE CASCADEOxirgi qatorga qarang: vazifalar o'ziga o'zi ishora qiladi (Referenced by — o'zi). Bunday bog'lanish — self-reference (o'ziga ishora).
Enum tartibi — e'lon tartibi. Kanon (PostgreSQL 18.6) — ikki natijada qatorlar tartibini solishtiring:
SELECT ustuvorlik, count(*) FROM vazifalar
GROUP BY 1 ORDER BY 1 DESC;
SELECT ustuvorlik::text AS matn, count(*) FROM vazifalar
GROUP BY 1 ORDER BY 1 DESC;Natija:
ustuvorlik | count
------------+-------
yuqori | 10
orta | 33
past | 4
(3 rows)
matn | count
--------+-------
yuqori | 10
past | 4
orta | 33
(3 rows)Enum sifatida: yuqori → orta → past — ma'noli tartib. Matnga aylantirilsa — alifbo: yuqori, past, orta. Shuning uchun ustuvorlik text + CHECK emas, enum. 47 ta vazifa: 40 + 7 kichik vazifa.
Yangi cheklovlar ishlaydi. Kanon (PostgreSQL 18.6):
UPDATE vazifalar SET bajarildi = true WHERE id = 1;
UPDATE vazifalar SET ustuvorlik = 'shoshilinch' WHERE id = 1;Natija:
ERROR: new row for relation "vazifalar" violates check constraint "vazifalar_bajarilgan_vaqt_check"
DETAIL: Failing row contains (1, 1, JavaScript kursining 26-qismini boshlash #o'qish, t, 2026-10-01 03:30:00+00, yuqori, 2026-10-31 18:59:00+00, null, null).
ERROR: invalid input value for enum ustuvorlik: "shoshilinch"
LINE 1: UPDATE vazifalar SET ustuvorlik = 'shoshilinch' WHERE id = 1...
^Birinchisi: "bajarildi" deb belgiladik, lekin bajarilgan_vaqt bermadik — baza ikki ustun zid bo'lishiga yo'l qo'ymadi. To'g'risi: SET bajarildi = true, bajarilgan_vaqt = now(). Ikkinchisi: enumda bunday qiymat yo'q — "noto'g'ri qiymat".
Va boshqa egasining vazifasiga ota qilib ulash — kanon (PostgreSQL 18.6): Key (ota_id, egasi_id)=(5, 1) is not present in table "vazifalar" — Azizning (1) vazifasi Dilnozaning 5-vazifasiga ulanmadi. Tarkibiy tashqi kalit ishladi.
6.4 Commit
git add sql/004-muddat-ustuvorlik.sql sql/005-namuna-muddat.sql
git commit -m "db: ustuvorlik (enum), muddat, bajarilgan vaqt" \
-m "Kichik vazifalar (004, 005-namuna)"Kanon commit xabari bitta qator: db: vazifalarga ustuvorlik (enum), muddat, bajarilgan vaqt va kichik vazifalar (004, 005-namuna) — 72 belgidan uzun bo'lgani uchun sarlavha va tanaga bo'ldik (commit xabarlari).
7. Ko'p uchraydigan xatolar
| Xato | Sabab | Davosi |
|---|---|---|
column "…" contains null values |
NOT NULL ustun DEFAULT siz |
DEFAULT bering yoki avval NULL, UPDATE, keyin SET NOT NULL |
cannot be cast automatically to type … |
Avtomatik o'girish yo'q | USING ifoda |
default for column … cannot be cast automatically |
Eski DEFAULT yangi turga mos emas |
Avval DROP DEFAULT |
check constraint "…" is violated by some row |
Mavjud qatorlar qoidaga zid | Topib tuzating; katta jadvalda NOT VALID + VALIDATE |
canceling statement due to lock timeout |
Qulf vaqtida olinmadi | Qayta urinish; uzoq tranzaksiyani toping |
Sayt "qotdi" ALTER paytida |
ACCESS EXCLUSIVE navbati |
lock_timeout, tez ALTER turlari |
relation "…" does not exist RENAME dan keyin |
Kod eski nomni ishlatadi | Expand–contract tartibi |
8. Mashqlar
1-mashq (oson): tez yoki sekin?
1 mln qatorli jadvalda qaysi ALTER faqat metadata'ni o'zgartiradi (tez), qaysi biri jadvalni qayta yozadi (sekin)?
ADD COLUMN arxiv boolean NOT NULL DEFAULT false→ADD COLUMN token uuid DEFAULT gen_random_uuid()→ALTER COLUMN narx TYPE bigint(integeredi) →RENAME COLUMN matn TO sarlavha→
Yechim
- tez — doimiy sukut, metadata'ga yoziladi.
- sekin — har qatorga boshqa uuid, jadval qayta yoziladi.
- sekin — har qator yangi formatda.
- tez — faqat nom; lekin ilova kodi buziladi.
Hammasi baribir ACCESS EXCLUSIVE oladi — "tez" ham navbat tuzog'iga tushishi mumkin. Shuning uchun lock_timeout.
2-mashq (o'rta): ixtiyoriy telefon
Holat 21 da foydalanuvchilar ga ixtiyoriy telefon ustunini qo'shing. Cheklov: telefon +998 90 000 00 01 shaklida bo'lsin (CHECK + regex ~, SELECT va WHERE). Aziz'ga to'g'ri raqam, Dilnoza'ga noto'g'ri (998900000002) yozib ko'ring. So'ng 1–3 foydalanuvchini chiqaring.
Yechim
ALTER TABLE foydalanuvchilar
ADD COLUMN telefon text,
ADD CONSTRAINT foydalanuvchilar_telefon_check
CHECK (telefon ~ '^\+998 \d{2} \d{3} \d{2} \d{2}$');
UPDATE foydalanuvchilar SET telefon = '+998 90 000 00 01'
WHERE email = 'aziz@example.com';
UPDATE foydalanuvchilar SET telefon = '998900000002'
WHERE email = 'dilnoza@example.com';
SELECT id, ism, telefon FROM foydalanuvchilar
WHERE id <= 3 ORDER BY id;Natija (biz ishga tushirdik, PG 17.6):
ALTER TABLE
UPDATE 1
ERROR: new row for relation "foydalanuvchilar" violates check constraint "foydalanuvchilar_telefon_check"
DETAIL: Failing row contains (2, dilnoza@example.com, Dilnoza Rahimova, 2026-09-03 09:20:00+00, 998900000002).
id | ism | telefon
----+------------------+-------------------
1 | Aziz Karimov | +998 90 000 00 01
2 | Dilnoza Rahimova |
3 | Sardor Tursunov |
(3 rows)CHECK va ADD COLUMN bitta ALTER da — mavjud 8 qatorda telefon NULL, NULL ~ '…' esa NULL va CHECK uni o'tkazadi (NULL mantiqi). Shuning uchun cheklov xatosiz qo'shildi va ixtiyoriylik saqlandi. + regex'da maxsus belgi — \+ bilan ekranlandi.
3-mashq (qiyin): enum'dan songa
Jamoa ustuvorlikni son (1 — past, 2 — o'rta, 3 — yuqori) qilib saqlashni taklif qildi. Holat 21 da buni tranzaksiya ichida sinab ko'ring: bitta ALTER TABLE bilan sukutni olib tashlang, turini smallint ga USING CASE … bilan o'zgartiring, yangi sukut 2 qo'ying va CHECK (ustuvorlik BETWEEN 1 AND 3) qo'shing. Natijani guruhlab ko'ring, so'ng ROLLBACK qilib, enum qaytganini tekshiring.
Yechim
BEGIN;
ALTER TABLE vazifalar
ALTER COLUMN ustuvorlik DROP DEFAULT,
ALTER COLUMN ustuvorlik TYPE smallint
USING CASE ustuvorlik
WHEN 'past' THEN 1
WHEN 'orta' THEN 2
WHEN 'yuqori' THEN 3
END,
ALTER COLUMN ustuvorlik SET DEFAULT 2,
ADD CONSTRAINT vazifalar_ustuvorlik_check
CHECK (ustuvorlik BETWEEN 1 AND 3);
SELECT ustuvorlik, count(*) FROM vazifalar
GROUP BY ustuvorlik ORDER BY ustuvorlik DESC;
ROLLBACK;
SELECT ustuvorlik, count(*) FROM vazifalar
GROUP BY ustuvorlik ORDER BY ustuvorlik DESC;Natija (biz ishga tushirdik, PG 17.6):
BEGIN
ALTER TABLE
ustuvorlik | count
------------+-------
3 | 10
2 | 33
1 | 4
(3 rows)
ROLLBACK
ustuvorlik | count
------------+-------
yuqori | 10
orta | 33
past | 4
(3 rows)Sanoqlar bir xil (10, 33, 4) — hech bir qator yo'qolmadi, aylantirish to'g'ri. DROP DEFAULT siz 'orta'::ustuvorlik sukuti smallint ga o'girilolmay xato berardi. ROLLBACK dan keyin hammasi joyida — PostgreSQL'da DDL ham tranzaksiyali. Haqiqiy migratsiyada ROLLBACK o'rniga COMMIT, va oldidan SET lock_timeout.
4-mashq: SQL qadami
sql/004-muddat-ustuvorlik.sql va sql/005-namuna-muddat.sql ni loyihangizga qo'shing, qo'llang va \d vazifalar ni «Tekshiruv» bo'limidagi bilan solishtiring. SELECT count(*) FROM vazifalar — 47 bo'lishi kerak. So'ng commit qiling.
Yechim
Kutilgan: 9 ustun, 4 indeks, 3 CHECK, 2 tashqi kalit. Referenced by da vazifalar o'zi turadi. Vazifalar 47 ta (40 + 7 kichik). 004 xato bersa — ko'pincha sabab UPDATE … SET bajarilgan_vaqt ni CHECK dan keyinga qo'yish (bajarilgan 20 ta vazifa yangi cheklovni buzadi) yoki faylning ikkinchi marta qo'llanishi (type "ustuvorlik" already exists).
9. Real ishda
- Migratsiyalar — har o'zgarish alohida, raqamlangan fayl (
004-…sql), bir marta qo'llanadi, o'zgartirilmaydi. Prisma Migrate, Drizzle Kit, node-pg-migrate kabi vositalar (27-qism) aynan shuALTERlarni yozadi va qaysi fayl qo'llanganini kuzatadi. - Zero-downtime deploy — katta kompaniyalarda
ALTERishlab turgan saytni to'xtatmasligi shart:lock_timeout+ qayta urinish,NOT VALID+VALIDATE,CREATE INDEX CONCURRENTLY, expand–contract. Ularni "safe migrations" deb atashadi. - Code review —
ALTER COLUMN … TYPEyokiSET NOT NULLkatta jadvalda ko'rilsa, reviewer darhol "bu qancha vaqt qulflaydi?" deb so'raydi. - Intervyu savoli: "Million qatorli jadvalga majburiy ustunni saytni to'xtatmasdan qanday qo'shasiz?" — doimiy
DEFAULT(PG 11+),lock_timeout; o'zgaruvchan qiymat bo'lsa — avvalNULLli ustun, partiyalabUPDATE, keyinNOT VALIDcheklov vaVALIDATE.
Xulosa
ALTER TABLE— ishlab turgan jadvalni ma'lumoti bilan o'zgartiradi; PostgreSQL'da tranzaksiyali (ROLLBACKqaytaradi).ADD COLUMN … NOT NULLfaqatDEFAULTbilan; doimiyDEFAULT— bir zumda, o'zgaruvchan — jadval qayta yoziladi.- Tur o'zgarishi —
TYPE … USING ifoda, eskiDEFAULTni oldin olib tashlang; ko'pincha jadval qayta yoziladi. - Cheklov mavjud qatorlarda tekshiriladi; katta jadvalda
NOT VALID→ ma'lumotni tuzatish →VALIDATE. - Ko'p
ALTER—ACCESS EXCLUSIVE(o'qish ham to'xtaydi) va navbat tuzog'i; himoya —lock_timeout. - Holat 21: enum
ustuvorlik,muddat,bajarilgan_vaqt, kichik vazifalar (tarkibiy tashqi kalit), 47 vazifa.
Keyingi dars: Ifodalar va o'rnatilgan funksiyalar — CASE, tur o'girish, matn funksiyalari, now(), date_trunc, interval va age — ma'lumotni ilovaga olib chiqmasdan bazada qayta ishlash.
Manbalar
- PostgreSQL 18 hujjati — ALTER TABLE (qulf darajalari,
NOT VALID, qayta yozish holatlari,USING) - PostgreSQL 18 hujjati — Explicit Locking (
ACCESS EXCLUSIVE) - PostgreSQL 18 hujjati — lock_timeout
- PostgreSQL 18 hujjati — Enumerated Types
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!