IlmHamroh
JavaScript Full-stack/26-qism. SQL va PostgreSQL21/64-dars22 daqiqa
Mundarija (30)

PostgreSQL ALTER TABLE: ustun qo'shish, turini o'zgartirish va qulflar

Qisqacha: ALTER TABLE ishlab 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 NULL ustun DEFAULT siz, buziladigan CHECK) rad etiladi. Ko'p ALTER jadvalga eng kuchli qulfni — ACCESS EXCLUSIVE ni oladi: tugaguncha jadvaldan o'qish ham to'xtaydi. Shuning uchun katta jadvalda tez buyruqlarni tanlaysiz (DEFAULT li ADD COLUMN, NOT VALID) va lock_timeout qo'yasiz.

Bu darsda

  • ADD, DROP, RENAME COLUMN, SET/DROP NOT NULL, SET DEFAULT ni yozasiz.
  • ALTER COLUMN … TYPE … USING bilan ustun turini ma'lumoti bilan birga o'zgartirasiz.
  • Mavjud ma'lumotga cheklov qo'shasiz, NOT VALID va VALIDATE farqini bilasiz.
  • ALTER qaysi qulfni olishini, qaysi buyruq tez va qaysi biri jadvalni qayta yozishini tushunasiz.
  • vazifalar ga 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):

sql
ALTER TABLE vazifalar ADD COLUMN ustuvorlik text NOT NULL;

Natija:

text
ERROR:  column "ustuvorlik" of relation "vazifalar" contains null values

Tarjimasi: "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):

sql
BEGIN;
ALTER TABLE vazifalar
  ADD COLUMN ustuvorlik text NOT NULL DEFAULT 'orta';
SELECT ustuvorlik, count(*) FROM vazifalar GROUP BY 1;
ROLLBACK;

Natija:

text
BEGIN
ALTER TABLE
 ustuvorlik | count 
------------+-------
 orta       |    40
(1 row)

ROLLBACK

Hamma 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:

sql
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:

text
BEGIN
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ROLLBACK

Qisqa 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):

sql
BEGIN;
ALTER TABLE vazifalar RENAME TO ishlar;
SELECT count(*) FROM vazifalar;
ROLLBACK;

Natija:

text
BEGIN
ALTER TABLE
ERROR:  relation "vazifalar" does not exist
LINE 1: SELECT count(*) FROM vazifalar;
                             ^
ROLLBACK

Ishlab 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):

sql
BEGIN;
ALTER TABLE vazifalar ALTER COLUMN bajarildi TYPE text;
SELECT DISTINCT bajarildi FROM vazifalar;
ALTER TABLE vazifalar ALTER COLUMN bajarildi TYPE boolean;
ROLLBACK;

Natija:

text
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".
ROLLBACK

boolean → 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:

sql
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:

text
BEGIN
ALTER TABLE
 bajarildi  | count 
------------+-------
 bajarilgan |    20
 faol       |    20
(2 rows)

ROLLBACK

Ikki narsaga qarang:

  • Bitta ALTER TABLE da bir nechta amal — vergul bilan. Hammasi bitta buyruq: jadval bir marta qulflanadi va bir marta o'qiladi.
  • DROP DEFAULT avval keldi. Eski sukut false (boolean) — yangi text turiga 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":

sql
ALTER TABLE vazifalar
  ADD CONSTRAINT qisqa CHECK (char_length(matn) <= 30);

Natija:

text
ERROR:  check constraint "qisqa" of relation "vazifalar" is violated by some row

Tarjimasi: "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:

  1. ADD CONSTRAINT … NOT VALID — cheklov qo'shiladi, lekin eski qatorlar tekshirilmaydi. Yangi INSERT/UPDATE lar darhol tekshiriladi. Bu bir zumda.
  2. VALIDATE CONSTRAINT … — eski qatorlarni tekshiradi. Bu uzoq, lekin kuchsizroq qulf oladi — o'qish va yozish davom etadi.

Kanon (PostgreSQL 18.6):

sql
BEGIN;
ALTER TABLE vazifalar
  ADD CONSTRAINT qisqa CHECK (char_length(matn) <= 30) NOT VALID;
ALTER TABLE vazifalar VALIDATE CONSTRAINT qisqa;
ROLLBACK;

Natija:

text
BEGIN
ALTER TABLE
ERROR:  check constraint "qisqa" of relation "vazifalar" is violated by some row
ROLLBACK

NOT 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 VALID bilan qo'shilgan CHECK bor jadvalda eski, qoidaga zid qatorni UPDATE qilsangiz (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):

sql
BEGIN;
ALTER TABLE vazifalar ADD COLUMN izoh text;
SELECT relation::regclass, mode, granted
FROM pg_locks WHERE relation = 'vazifalar'::regclass;
ROLLBACK;

Natija:

text
BEGIN
ALTER TABLE
 relation  |        mode         | granted 
-----------+---------------------+---------
 vazifalar | AccessExclusiveLock | t
(1 row)

ROLLBACK

pg_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 mumkin

Diagrammaning 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):

sql
SET lock_timeout = '1s';
SELECT count(*) FROM vazifalar;

Natija:

text
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):

ALTER TABLE, 1 mln qator
  • 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):

sql
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:

bash
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
done

6.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:

text
                                     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 CASCADE

Oxirgi 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:

sql
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:

text
 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):

sql
UPDATE vazifalar SET bajarildi = true WHERE id = 1;
UPDATE vazifalar SET ustuvorlik = 'shoshilinch' WHERE id = 1;

Natija:

text
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

bash
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 (integer edi) →
  • 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
sql
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):

text
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
sql
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):

text
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 shu ALTER larni yozadi va qaysi fayl qo'llanganini kuzatadi.
  • Zero-downtime deploy — katta kompaniyalarda ALTER ishlab 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 … TYPE yoki SET NOT NULL katta 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 — avval NULL li ustun, partiyalab UPDATE, keyin NOT VALID cheklov va VALIDATE.

Xulosa

  • ALTER TABLE — ishlab turgan jadvalni ma'lumoti bilan o'zgartiradi; PostgreSQL'da tranzaksiyali (ROLLBACK qaytaradi).
  • ADD COLUMN … NOT NULL faqat DEFAULT bilan; doimiy DEFAULT — bir zumda, o'zgaruvchan — jadval qayta yoziladi.
  • Tur o'zgarishi — TYPE … USING ifoda, eski DEFAULT ni 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

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
PostgreSQL ALTER TABLE: ustun qo'shish, turini o'zgartirish va qulflar — IlmHamroh