IlmHamroh
JavaScript Full-stack/26-qism. SQL va PostgreSQL12/64-dars19 daqiqa
Mundarija (39)

CREATE TABLE va cheklovlar: qoidalarni bazaning o'ziga yozish

Qisqacha: CREATE TABLE jadvalni yaratadi: har ustunning nomi, turi va qoidalari. Cheklovlar (constraints) — bazaning o'zi kuzatadigan qoidalar: NOT NULL (bo'sh bo'lmaydi), DEFAULT (berilmasa — shu qiymat), PRIMARY KEY (noyob va bo'sh emas), UNIQUE (takrorlanmaydi), CHECK (shart bajarilsin). Ilova kodida xato bo'lsa ham, qo'lda SQL yozilsa ham, boshqa xizmat ulansa ham — qoidani buzgan qator bazaga kirmaydi. Har cheklovga aniq nom bering: xato xabarida aynan shu nom chiqadi.

Bu darsda

  • CREATE TABLE bilan ustunlar, turlar va sukut qiymatlarni yozasiz.
  • NOT NULL, PRIMARY KEY, UNIQUE, CHECK cheklovlarini qo'yasiz va ularning xato xabarlarini o'qiysiz.
  • Registrga befarq noyob email (lower(email) ustidagi noyob indeks) nega kerakligini tushuntirasiz.
  • Cheklovlarni nomlash kelishuvini, IF NOT EXISTS tuzog'ini va schema (namespace) nima ekanini bilasiz.
  • vazifalar bazasining birinchi sxema faylini (001-jadvallar.sql) qo'llab, commit qilasiz.

Oldin bilishingiz kerak: PostgreSQL ma'lumot turlari, SQL tili va buyruq turlari, psql chuqur.

1. Nega bu kerak?

22-qismdagi vazifalar frontend'ida vazifa matni bo'sh bo'lsa yoki 100 belgidan oshsa, forma xato ko'rsatardi. Bu qoida endi bazada ham kerakmi? Axir frontend tekshirib bo'ldi-ku.

Kerak. Bazaga faqat sizning formangiz yozmaydi:

  • 25-qismdagi API'ga curl bilan istalgan JSON yuborish mumkin — forma umuman ishtirok etmaydi.
  • Ertaga mobil ilova yoki Telegram bot ham shu bazaga yozadi — ularning tekshiruvi boshqa dasturchining qo'lida.
  • Admin psql'da qo'lda UPDATE qiladi va bitta harfni adashtiradi.
  • API kodida xato: yangi versiyada tekshiruv tasodifan o'chib qoladi.

Shu holatlarning har birida noto'g'ri qator bazaga tushadi va yillab qoladi. Bazadagi cheklov esa oxirgi himoya chizig'i: kim, qanday yo'l bilan yozmasin, qoidani buzgan qator kirmaydi.

flowchart LR
  F[Forma] --> B
  A[API, curl] --> B
  P[Admin, psql] --> B
  B{Baza<br/>cheklovlari} -->|hammasi bajarildi| S[(Qator saqlandi)]
  B -->|bittasi buzildi| E[ERROR:<br/>qator kirmadi]

Nimaga qarang: yo'l qaysi bo'lmasin, hammasi bitta "eshik" — bazadagi cheklovlar orqali o'tadi. Bitta qoida buzilsa, butun qator rad etiladi.

Hayotiy o'xshatish: metro turniketi. Kassir chipta sotayotganda tekshiradi (frontend), nazoratchi ham qaraydi (API). Lekin turniket chiptasiz odamni baribir o'tkazmaydi (baza). Kassir adashsa ham, nazoratchi chalg'isa ham.

2. CREATE TABLE anatomiyasi

2.1 Eng oddiy ko'rinish

text
CREATE TABLE jadval_nomi (
  ustun_nomi  tur  [cheklovlar],
  ...
  [jadval darajasidagi cheklovlar]
);

Har ustun — nom, tur va ixtiyoriy cheklovlar. Ustunlar vergul bilan ajratiladi, oxirgisidan keyin vergul yo'q (JS massividan farqli — u yerda oxirgi vergul ruxsat edi). PostgreSQL ma'lumot turlari darsida turlarni tanladik, endi ularga qoidalar qo'shamiz.

2.2 Kanon jadvali — vazifalar

Mana kanondagi vazifalar jadvali. Har qismini quyida birma-bir ochamiz:

sql
CREATE TABLE vazifalar (
  id              bigint      GENERATED ALWAYS AS IDENTITY,
  egasi_id        bigint      NOT NULL,
  matn            text        NOT NULL,
  bajarildi       boolean     NOT NULL DEFAULT false,
  yaratilgan_vaqt timestamptz NOT NULL DEFAULT now(),
  CONSTRAINT vazifalar_pkey PRIMARY KEY (id),
  CONSTRAINT vazifalar_matn_check
    CHECK (matn = btrim(matn) AND char_length(matn) BETWEEN 1 AND 100)
);
text
CREATE TABLE
Qism Nima qiladi
GENERATED ALWAYS AS IDENTITY id ni baza o'zi beradi: 1, 2, 3…
NOT NULL bo'sh (NULL) qiymat taqiqlanadi
DEFAULT false, DEFAULT now() qiymat berilmasa — shu
CONSTRAINT … PRIMARY KEY (id) birlamchi kalit (primary key), nomi bilan
CONSTRAINT … CHECK (…) matn qoidasi, nomi bilan

egasi_id hozircha oddiy son: unga hali tashqi kalit qo'yilmagan. Uni Foreign key va referential actions darsida qo'shamiz — va FK'siz nima buzilishini o'sha yerda ko'ramiz.

3. NOT NULL va DEFAULT

3.1 NOT NULL

Postgres'da ustun sukut bo'yicha NULL ni qabul qiladi. NOT NULL yozilmasa, "matnsiz vazifa" yoki "egasiz vazifa" bazaga bemalol kiradi. Kanonda foydalanuvchilar jadvaliga ismsiz qator yozib ko'rildi:

sql
INSERT INTO foydalanuvchilar (email) VALUES ('sardor@example.com');
text
ERROR:  null value in column "ism" of relation "foydalanuvchilar" violates not-null constraint
DETAIL:  Failing row contains (5, sardor@example.com, null, 2026-10-24 04:55:19.842156+00).

Tarjimasi: "foydalanuvchilar jadvalining ism ustunidagi null qiymat not-null cheklovini buzadi. Batafsil: xatoga sabab bo'lgan qator — …". DETAIL qatori butun qatorni ko'rsatadi — qaysi qiymat muammo ekanini topish oson. Undagi id va vaqt sizda boshqacha chiqadi: ular kanon ishga tushirilgan paytga bog'liq.

Qoida: avval hamma ustunni NOT NULL deb o'ylang, keyin "bu haqiqatan ixtiyoriymi?" deb so'rang. NULL ning o'z murakkab mantiqi bor (NULL mantiqi) — u faqat "haqiqatan noma'lum" bo'lganda kerak.

3.2 DEFAULT

DEFAULT — qiymat berilmaganda ishlatiladigan qiymat. Kanon vazifani faqat egasi_id va matn bilan qo'shdi, RETURNING * bilan natijani so'radi (RETURNING ni INSERT va RETURNING darsida o'rganamiz):

sql
INSERT INTO vazifalar (egasi_id, matn) VALUES (1, 'Non olish')
RETURNING *;
text
 id | egasi_id |   matn    | bajarildi |        yaratilgan_vaqt
----+----------+-----------+-----------+-------------------------------
  1 |        1 | Non olish | f         | 2026-10-24 04:55:19.843836+00
(1 row)

INSERT 0 1

id — 1 (identity), bajarildi — f (DEFAULT false), yaratilgan_vaqt — joriy vaqt (DEFAULT now()). Ilova bu maydonlarni yubormaydi — baza o'zi to'ldiradi. Diqqat: DEFAULT faqat ustun umuman berilmaganda ishlaydi. bajarildi ga ochiqchasiga NULL yuborsangiz, DEFAULT emas, NOT NULL xatosi chiqadi.

4. PRIMARY KEY

4.1 Ikki qoida bitta so'zda

PRIMARY KEY (birlamchi kalit) = UNIQUE + NOT NULL + avtomatik indeks. Har jadvalda ko'pi bilan bitta PK bo'ladi. U Relatsion model darsidagi "qatorning pasporti".

4.2 Identity — id ni baza beradi

GENERATED ALWAYS AS IDENTITY — Postgres raqamlarni ketma-ket beradi va qo'lda yozishni taqiqlaydi:

sql
INSERT INTO foydalanuvchilar (id, email, ism)
VALUES (100, 'sardor@example.com', 'Sardor');
text
ERROR:  cannot insert a non-DEFAULT value into column "id"
DETAIL:  Column "id" is an identity column defined as GENERATED ALWAYS.
HINT:  Use OVERRIDING SYSTEM VALUE to override.

Tarjimasi: "id ustuniga DEFAULT bo'lmagan qiymat yozib bo'lmaydi. U GENERATED ALWAYS deb ta'riflangan identity ustun. Maslahat: chetlab o'tish uchun OVERRIDING SYSTEM VALUE ishlating". Bu himoya: qo'lda yozilgan id keyinchalik baza bergan raqam bilan to'qnashardi. HINT dagi chetlab o'tish yo'li faqat maxsus holatlar (ma'lumot ko'chirish) uchun. serial, uuid va boshqa variantlarni keyingi darsda — Primary key tanlovi da solishtiramiz.

5. UNIQUE

5.1 Takror email

Bitta email bilan ikki akkaunt bo'lmasligi kerak. Odatiy yo'l — UNIQUE (email). Lekin Aziz@Example.COM va aziz@example.com — bitta pochta qutisi, UNIQUE esa ularni har xil satr deb biladi.

Kanon yechimi — ifoda ustida noyob indeks:

sql
CREATE UNIQUE INDEX foydalanuvchilar_email_key
  ON foydalanuvchilar (lower(email));
text
CREATE INDEX

lower(email) — emailning kichik harfli ko'rinishi. Indeks shu ko'rinish bo'yicha noyoblikni tekshiradi. Tekshiruv:

sql
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('aziz@example.com', 'Aziz Karimov');
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('Aziz@Example.COM', 'Boshqa Aziz');
text
INSERT 0 1
ERROR:  duplicate key value violates unique constraint "foydalanuvchilar_email_key"
DETAIL:  Key (lower(email))=(aziz@example.com) already exists.

Tarjimasi: "takror kalit qiymati foydalanuvchilar_email_key noyoblik cheklovini buzadi. (lower(email))=(aziz@example.com) kaliti allaqachon bor". Email asl ko'rinishida saqlanadi (foydalanuvchi qanday yozgan bo'lsa), noyoblik esa registrsiz.

Nega oddiy UNIQUE (lower(email)) emas? Jadval cheklovi UNIQUE faqat ustun nomlarini qabul qiladi, ifodani emas. Ifoda bo'yicha noyoblik — faqat CREATE UNIQUE INDEX bilan. Indekslarni Indeks nima: B-tree ichidan darsida chuqur o'rganamiz.

5.2 UNIQUE va NULL

Bitta muhim tafsilot: oddiy UNIQUE ustunda bir nechta NULL bo'lishi mumkin — NULL hech narsaga, hatto boshqa NULL ga ham "teng emas". Bu xatti-harakatni NULL mantiqi darsida kanon misoli bilan ko'ramiz.

Tekshirib ko'ring: vazifalar jadvaliga UNIQUE (egasi_id, matn) qo'shsak, nima o'zgaradi? Ikki xil foydalanuvchi bir xil matnli vazifa yoza oladimi?

Javob

Bitta egasida bir xil matnli ikki vazifa bo'lolmaydi, lekin ikki xil egada bo'la oladi — noyoblik juftlik bo'yicha: (1, 'Non olish') va (2, 'Non olish') har xil juftlik. Bunday tarkibiy noyoblik (bir nechta ustun bo'yicha) ham UNIQUE bilan yoziladi. Kanon aynan shunga o'xshash qoidani (registrga befarq, o'chirilganlarni hisobga olmay) keyinroq qo'shadi — Soft delete, timestamps va tarix jadvallari darsida.

6. CHECK

6.1 Istalgan shart

CHECK (shart) — har qator uchun shart true (yoki NULL) bo'lishi kerak. Kanondagi uchta CHECK:

text
CONSTRAINT foydalanuvchilar_email_check
  CHECK (email ~ '^[^@[:space:]]+@[^@[:space:]]+$'),
CONSTRAINT foydalanuvchilar_ism_check
  CHECK (btrim(ism) <> '' AND char_length(ism) <= 100),
CONSTRAINT vazifalar_matn_check
  CHECK (matn = btrim(matn) AND char_length(matn) BETWEEN 1 AND 100)
  • Email: ~ — muntazam ifoda (regex) bilan solishtirish. Shart: @ dan oldin va keyin bo'shliqsiz, @ siz belgilar bor. Regex'ni RegExp asoslari va metodlari darsida o'rgangansiz.
  • Ism: btrim(ism) — chetidagi bo'shliqlar olib tashlangan ism. U bo'sh bo'lmasin va 100 belgidan oshmasin.
  • Matn: chetida bo'shliq yo'q (matn = btrim(matn)), uzunligi 1–100. Bu — 22-qismdagi frontend qoidasi (Vazifa.MAKS_UZUNLIK = 100), endi bazada.

Email tekshiruvi ataylab yumshoq: to'liq email qoidalari juda murakkab, ular ilovada (Zod va boshqalar). Baza faqat "aniq email emas" narsani to'xtatadi. Izoh kanon faylining o'zida yozilgan.

6.2 Cheklovlarni ataylab buzamiz

Kanon har qoidani ataylab buzdi. Uchta misol:

sql
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('aziz-example.com', 'Aziz');
INSERT INTO vazifalar (egasi_id, matn) VALUES (1, ' Non olish ');
INSERT INTO vazifalar (egasi_id, matn) VALUES (1, repeat('a', 101));
text
ERROR:  new row for relation "foydalanuvchilar" violates check constraint "foydalanuvchilar_email_check"
ERROR:  new row for relation "vazifalar" violates check constraint "vazifalar_matn_check"
ERROR:  new row for relation "vazifalar" violates check constraint "vazifalar_matn_check"

(DETAIL qatorlari qisqartirildi.) Har xatoda qaysi cheklov buzilgani nomi bilan aytilgan. repeat('a', 101) — 101 ta "a" harfi. Chegaradagi qiymat esa o'tdi:

sql
INSERT INTO vazifalar (egasi_id, matn) VALUES (1, repeat('oʻ', 50));
text
INSERT 0 1

'oʻ' — ikki belgi (ʻ — 2 bayt, lekin 1 belgi), 50 marta — aynan 100 belgi. char_length belgilarni sanaydi, baytlarni emas — o'zbekcha matn uchun aynan shu kerak.

6.3 JS trim() va btrim() farqi

Kanon qayd etgan nozik farq: JS'dagi trim() hamma bo'shliq turlarini (tab, yangi qator, uzilmas bo'sh joy) kesadi. Postgres'dagi btrim(matn) esa sukut bo'yicha faqat oddiy probelni. Ya'ni boshida tab bo'lgan matn frontend'da "bo'sh joysiz" ga aylanadi, bazaga to'g'ridan-to'g'ri yozilsa esa cheklovdan o'tadi. Uzunlikni sanashda ham farq bor: JS'da emoji length — 2, Postgres'da char_length — 1. Kanon buni ongli ravishda qoldirgan: ilova trim() qilib yuboradi, baza — oxirgi chiziq.

Tekshirib ko'ring: CHECK (narx > 0) bo'lgan ustunga NULL yozilsa nima bo'ladi?

Javob

Qator o'tadi. NULL > 0 natijasi true ham, false ham emas — NULL (noma'lum). CHECK faqat false ni rad etadi. Shuning uchun majburiy ustunda CHECK bilan birga NOT NULL ham yoziladi. 3-mashqdagi narx ustuniga qarang.

7. Cheklovlarni nomlash

7.1 Nega nom kerak

Nom bermasangiz, Postgres o'zi beradi. Lekin nom — xato xabaridagi yagona ishora. API kodi xatoni foydalanuvchiga tushunarli javobga aylantiradi:

Cheklov nomi Foydalanuvchiga
foydalanuvchilar_email_key "Bu email bilan akkaunt bor"
vazifalar_matn_check "Vazifa matni 1–100 belgi bo'lsin"

Nom tasodifiy bo'lsa yoki migratsiyada o'zgarsa, bu xarita buziladi. Shuning uchun kanon hamma nomni aniq yozadi va Postgres'ning o'z andozasiga amal qiladi:

text
<jadval>_<ustun>_<tur>

pkey — primary key, key — unique, fkey — foreign key, check — check, idx — oddiy indeks. Postgres o'zi nom berganda ham shu andozani ishlatadi — shuning uchun ikkala usul bir xil ko'rinadi.

7.2 PostgreSQL 18: NOT NULL ham nomli

Postgres hamma cheklovlarni pg_constraint nomli ichki jadvalda (katalogda) saqlaydi. Uni oddiy SELECT bilan o'qish mumkin. 'vazifalar'::regclass — "vazifalar jadvali" degan ma'no. Avval PostgreSQL 17.6 da (biz ishga tushirdik):

sql
SELECT conname FROM pg_constraint
WHERE conrelid = 'vazifalar'::regclass
ORDER BY conname;
text
       conname
----------------------
 vazifalar_matn_check
 vazifalar_pkey
(2 rows)

Faqat ikkita: birlamchi kalit va CHECK. Endi kanon PostgreSQL 18.6 dagi ro'yxat — unda NOT NULL cheklovlari ham nom bilan chiqdi:

text
             conname
---------------------------------
 vazifalar_bajarildi_not_null
 vazifalar_egasi_id_not_null
 vazifalar_id_not_null
 vazifalar_matn_check
 vazifalar_matn_not_null
 vazifalar_pkey
 vazifalar_yaratilgan_vaqt_not_null

(Kanon chiqishidan vazifalar qismi, faqat conname ustuni.) Nima bo'ldi? Bu PostgreSQL 18 yangiligi: NOT NULL endi katalogda alohida, nomli cheklov sifatida saqlanadi (hujjatdagi PG 18 reliz eslatmalari). 17-versiyada u faqat ustunning belgisi edi — shuning uchun yuqoridagi ro'yxatda yo'q. Noyob indeks (foydalanuvchilar_email_key) esa ro'yxatda yo'q — u cheklov emas, indeks; lekin xato xabari uni baribir "unique constraint" deb ataydi.

8. IF NOT EXISTS va schema

8.1 IF NOT EXISTS tuzog'i

CREATE TABLE IF NOT EXISTS — jadval bo'lsa, xato bermaydi:

sql
CREATE TABLE IF NOT EXISTS vazifalar (id int);
text
NOTICE:  relation "vazifalar" already exists, skipping
CREATE TABLE

Tarjimasi: "vazifalar munosabati allaqachon bor, o'tkazib yuborildi". Diqqat: vazifalar da 5 ta ustun bor, bu buyruqda esa bitta id int. Postgres tuzilmani solishtirmadi — faqat nomni tekshirdi va "CREATE TABLE" deb javob berdi. Sxema faylini "bir necha marta ishga tushirsa bo'ladigan" qilish uchun IF NOT EXISTS qulay tuyuladi, lekin u jadval eskirganini yashiradi. Kanon buning o'rniga raqamli fayllarni bir marta qo'llaydi.

8.2 Schema — jadvallar "papkasi"

Postgres'da schema (namespace) — bitta baza ichidagi nomlar guruhi, fayl tizimidagi papka kabi. Sukut bo'yicha hamma narsa public schema'sida. Bir xil nomli jadval turli schema'larda bo'lishi mumkin:

sql
CREATE SCHEMA hisobot;
CREATE TABLE hisobot.vazifalar (kun date, soni int);

Natija:

text
CREATE SCHEMA
CREATE TABLE

\dt *.vazifalar (hamma schema'lardagi vazifalar):

text
             List of tables
 Schema  |   Name    | Type  |  Owner
---------+-----------+-------+----------
 hisobot | vazifalar | table | postgres
 public  | vazifalar | table | postgres
(2 rows)

Schema nomisiz yozilgan vazifalar qaysi biri? Buni search_path sozlamasi hal qiladi — Postgres qaysi schema'larda qaysi tartibda qidiradi. Sozlamani SHOW ko'rsatadi:

sql
SHOW search_path;
text
   search_path
-----------------
 "$user", public
(1 row)

Avval foydalanuvchi nomidagi schema (bizda yo'q), keyin public. Kichik loyihada public yetadi. Katta tizimlarda schema'lar modullarni (hisobot, arxiv) yoki ruxsatlarni ajratadi. Kanon tajribadan keyin DROP SCHEMA hisobot CASCADE bilan uni ichidagi jadval bilan birga o'chirdi.

9. SQL qadami: 001-jadvallar.sql

Endi vazifalar loyihasining birinchi sxema fayli. Kanon fayli — foydalanuvchilar va vazifalar jadvallari (vazifalar qismini «Kanon jadvali — vazifalar» bo'limida ko'rdingiz):

sql
-- 001-jadvallar.sql — birinchi ikki jadval va cheklovlar
CREATE TABLE foydalanuvchilar (
  id              bigint      GENERATED ALWAYS AS IDENTITY,
  email           text        NOT NULL,
  ism             text        NOT NULL,
  yaratilgan_vaqt timestamptz NOT NULL DEFAULT now(),
  CONSTRAINT foydalanuvchilar_pkey PRIMARY KEY (id),
  -- to'liq email tekshiruvi — ilova ishi
  CONSTRAINT foydalanuvchilar_email_check
    CHECK (email ~ '^[^@[:space:]]+@[^@[:space:]]+$'),
  CONSTRAINT foydalanuvchilar_ism_check
    CHECK (btrim(ism) <> '' AND char_length(ism) <= 100)
);

-- Email registrga befarq noyob
CREATE UNIQUE INDEX foydalanuvchilar_email_key
  ON foydalanuvchilar (lower(email));

-- vazifalar jadvali — «Kanon jadvali» bo'limidagidek

Kanon faylida izohlar uzunroq; ma'nosi saqlangan holda qisqartirdik. Faylni yaratganingizdan keyin vazifalar jadvalini ham o'sha bo'limdagidek shu faylga qo'shing.

Qo'llash — psql chuqur darsidagidek, bitta tranzaksiyada va birinchi xatoda to'xtab:

bash
psql -X -1 -v ON_ERROR_STOP=1 \
  -d postgres://postgres@127.0.0.1:5499/vazifalar \
  -f sql/001-jadvallar.sql

Tekshiruv — \d vazifalar. Kanon chiqishi:

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()
Indexes:
    "vazifalar_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "vazifalar_matn_check" CHECK (matn = btrim(matn) AND char_length(matn) >= 1 AND char_length(matn) <= 100)

Postgres BETWEEN 1 AND 100 ni >= 1 AND <= 100 ga aylantirib saqlagan — ma'nosi bir xil. Kanon holatni namunasiz ham, namuna bilan ham noldan qurib tekshirgan: ikkalasi exit 0, ikkala jadvalda hozircha 0 qator.

Commit:

bash
git add sql/001-jadvallar.sql
M='db: foydalanuvchilar va vazifalar jadvallari'
M+=' — cheklovlar bilan (001)'
git commit -m "$M"

Shu paytdan boshlab 001-jadvallar.sql o'zgartirilmaydi. Keyingi o'zgarishlar yangi fayllarda (002-…) — chunki bu fayl allaqachon bazalarga qo'llangan.

10. Ko'p uchraydigan xatolar

10.1 Oxirgi ustundan keyin vergul

text
ERROR:  syntax error at or near ")"

bajarildi boolean, dan keyin ) — Postgres yana ustun kutgan edi. Oxirgi qatordagi vergulni olib tashlang.

10.2 NOT NULL siz ustunlar

Jadval yaratildi, hammasi ishlaydi — oylar o'tib NULL li qatorlar topiladi. Majburiy ustunlarga darhol NOT NULL. Keyin qo'shish uchun avval mavjud NULL larni tozalash kerak bo'ladi (ALTER TABLE va sxemani o'zgartirish).

10.3 Registrga sezgir noyob email

UNIQUE (email) — Ali@x.com va ali@x.com ikki akkaunt bo'ladi. Ifoda ustidagi noyob indeks: (lower(email)).

10.4 Faqat frontend tekshiruvi

"Forma tekshiradi" — lekin API, skript va admin formadan o'tmaydi. Asosiy qoidalar bazada ham bo'lsin.

10.5 Nomsiz cheklovlar

CHECK (narx > 0) nomsiz — Postgres taomlar_narx_check deb nomlaydi. Ikkinchi nomsiz CHECK esa taomlar_narx_check1 bo'ladi va xato xabaridan qaysi biri ekanini bilish qiyinlashadi. Aniq nom bering.

11. Mashqlar

Mashqlar kurs/mashqlar/26/12-create-table/ da.

1-mashq (oson): cheklovni tanlang

  1. Ustun bo'sh bo'lmasin — .
  2. Qiymat berilmasa false bo'lsin — false.
  3. Narx musbat bo'lsin — (narx > 0).
  4. Ikki qatorda bir xil telefon bo'lmasin — .
  5. Noyob va bo'sh emas, qatorni aniqlaydi — .
Yechim

NOT NULL, DEFAULT false, CHECK (narx > 0), UNIQUE (telefon), PRIMARY KEY. Telefon raqamini solishtirishdan oldin bir xil ko'rinishga keltirish (bo'shliqlarsiz) foydali — aks holda +998 90 … va +99890… har xil bo'ladi.

2-mashq (o'rta): qaysi biri o'tadi?

Bo'sh bazada 001-jadvallar.sql qo'llangan (FK hali yo'q). Ketma-ket olti INSERT. Har biri o'tadimi? O'tmasa — qaysi cheklov?

sql
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('malika@example.com', 'Malika');
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('MALIKA@example.com', 'Malika 2');
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('jasur @example.com', 'Jasur');
INSERT INTO vazifalar (egasi_id, matn) VALUES (1, 'Choy damlash');
INSERT INTO vazifalar (egasi_id, matn, bajarildi)
VALUES (1, 'Non', NULL);
INSERT INTO vazifalar (egasi_id, matn) VALUES (77, 'Egasi yo''q');
Yechim

Biz ishga tushirgan natija (PostgreSQL 17.6, psql -q — muvaffaqiyatli INSERT lar jim). Xatolar qisqa shaklda (\set VERBOSITY terse — DETAIL qatorlarisiz). Oxiriga ikkita tekshiruv so'rovi qo'shdik: SELECT count(*) AS foydalanuvchi FROM foydalanuvchilar; va SELECT id, egasi_id, matn, bajarildi FROM vazifalar ORDER BY id;:

text
ERROR:  duplicate key value violates unique constraint "foydalanuvchilar_email_key"
ERROR:  new row for relation "foydalanuvchilar" violates check constraint "foydalanuvchilar_email_check"
ERROR:  null value in column "bajarildi" of relation "vazifalar" violates not-null constraint
 foydalanuvchi
---------------
             1
(1 row)

 id | egasi_id |     matn     | bajarildi
----+----------+--------------+-----------
  1 |        1 | Choy damlash | f
  3 |       77 | Egasi yo'q   | f
(2 rows)
  1. O'tdi. 2. Yo'q — registrga befarq takror. 3. Yo'q — emailda bo'shliq. 4. O'tdi, bajarildi — DEFAULT false. 5. Yo'q — ochiq NULL DEFAULT ni ishga tushirmaydi. 6. O'tdi — 77-foydalanuvchi yo'q, lekin egasi_id hali oddiy son. Bu teshikni Foreign key va referential actions darsida yopamiz.

Yana bir narsa: ikkinchi vazifaning id si 3 — 2-raqam muvaffaqiyatsiz INSERT da "kuydi". Bu haqda keyingi darsda.

3-mashq (qiyin): «Bahor» menyusi

taomlar jadvalini yarating: id (identity), nom (chetida bo'shliqsiz, 1–60 belgi, registrga befarq noyob), narx (so'm, tiyin bilan, 0 dan katta va 1 000 000 dan kichik, majburiy), bormi (sukut true). Hamma cheklovga nom bering. To'rt taom qo'shing (osh 35 000, lag'mon 28 000, manti 30 000, ko'k choy 5 000). Keyin to'rtta noto'g'ri qatorni sinang: OSH, narxi 0, nomi bo'shliq bilan boshlangan, narxsiz.

Yechim
sql
CREATE TABLE taomlar (
  id      bigint        GENERATED ALWAYS AS IDENTITY,
  nom     text          NOT NULL,
  narx    numeric(12,2) NOT NULL,
  bormi   boolean       NOT NULL DEFAULT true,
  CONSTRAINT taomlar_pkey PRIMARY KEY (id),
  CONSTRAINT taomlar_nom_check
    CHECK (nom = btrim(nom) AND char_length(nom) BETWEEN 1 AND 60),
  CONSTRAINT taomlar_narx_check CHECK (narx > 0 AND narx < 1000000)
);
CREATE UNIQUE INDEX taomlar_nom_key ON taomlar (lower(nom));

INSERT INTO taomlar (nom, narx) VALUES
  ('Osh', 35000), ('Lag''mon', 28000), ('Manti', 30000),
  ('Ko''k choy', 5000);
INSERT INTO taomlar (nom, narx) VALUES ('OSH', 36000);
INSERT INTO taomlar (nom, narx) VALUES ('Somsa', 0);
INSERT INTO taomlar (nom, narx) VALUES (' Somsa', 8000);
INSERT INTO taomlar (nom) VALUES ('Somsa');
SELECT id, nom, narx, bormi FROM taomlar ORDER BY id;

Natija (PostgreSQL 17.6, psql -q — muvaffaqiyatli buyruqlar jim, faqat xatolar va SELECT):

text
ERROR:  duplicate key value violates unique constraint "taomlar_nom_key"
DETAIL:  Key (lower(nom))=(osh) already exists.
ERROR:  new row for relation "taomlar" violates check constraint "taomlar_narx_check"
DETAIL:  Failing row contains (6, Somsa, 0.00, t).
ERROR:  new row for relation "taomlar" violates check constraint "taomlar_nom_check"
DETAIL:  Failing row contains (7,  Somsa, 8000.00, t).
ERROR:  null value in column "narx" of relation "taomlar" violates not-null constraint
DETAIL:  Failing row contains (8, Somsa, null, t).
 id |    nom    |   narx   | bormi
----+-----------+----------+-------
  1 | Osh       | 35000.00 | t
  2 | Lag'mon   | 28000.00 | t
  3 | Manti     | 30000.00 | t
  4 | Ko'k choy |  5000.00 | t
(4 rows)

Har noto'g'ri qator o'z cheklovi nomi bilan rad etildi. narx dagi NOT NULL muhim: usiz narxsiz qator CHECK dan o'tib ketardi («CHECK» bo'limidagi savol). DETAIL dagi id lar (6, 7, 8) — rad etilgan urinishlar ham raqam "yeydi".

4-mashq: SQL qadami

«SQL qadami» bo'limini bajaring: sql/001-jadvallar.sql faylini yarating, o'z bazangizga qo'llang, \d foydalanuvchilar va \d vazifalar bilan tekshiring, commit qiling. Keyin kanondagi 8 ta buzilishdan kamida uchtasini o'z bazangizda takrorlang va xato xabaridagi cheklov nomlarini yozing.

Yechim

\d foydalanuvchilar da Indexes: ostida foydalanuvchilar_pkey va foydalanuvchilar_email_key … (lower(email)), Check constraints: ostida ikkita CHECK bo'lishi kerak. Buzilishlar va nomlar: takror email — foydalanuvchilar_email_key, @ siz email — foydalanuvchilar_email_check, bo'sh ism — foydalanuvchilar_ism_check, bo'sh yoki 101 belgili matn — vazifalar_matn_check. Sinovdan keyin bazani tozalash uchun: TRUNCATE vazifalar, foydalanuvchilar RESTART IDENTITY; (faqat o'z o'quv bazangizda!). Namuna ma'lumot INSERT va RETURNING darsida qo'shiladi.

12. Real ishda

  • Ma'lumot sifati: yirik kompaniyalarda "iflos ma'lumot"ni tozalashga butun jamoalar ishlaydi. Ko'p muammo boshida bitta NOT NULL yoki CHECK yozilmaganidan.
  • API va xatolar: backend Postgres xatosining kodi (23505 — noyoblik, 23514 — check, 23502 — not null) va cheklov nomiga qarab HTTP javob beradi: 409 Conflict, 400/422. Buni node-postgres (pg) asoslari darsida qilamiz.
  • ORM bilan: Prisma, Drizzle sxemasida ham @unique, @default, check bor — ular aynan shu SQL'ni yaratadi. Ba'zi cheklovlarni (ifoda ustidagi noyoblik, murakkab CHECK) ORM qo'llamaydi va qo'lda migratsiya yoziladi.
  • Intervyu savollari: "Validatsiya frontend'da bo'lsa, bazada nega kerak?", "UNIQUE va PRIMARY KEY farqi?", "Registrga befarq noyob email qanday qilinadi?".

Xulosa

  • CREATE TABLE: ustun = nom + tur + cheklovlar; oxirgi ustundan keyin vergul yo'q.
  • NOT NULL — majburiy; DEFAULT — berilmasa ishlaydi (ochiq NULL da emas); PRIMARY KEY = noyob + bo'sh emas + indeks.
  • UNIQUE ustun bo'yicha; registrga befarq email — CREATE UNIQUE INDEX … (lower(email)).
  • CHECK — istalgan shart; NULL uni o'tadi, shuning uchun NOT NULL bilan birga; char_length belgini sanaydi.
  • Cheklov nomlari — <jadval>_<ustun>_<pkey|key|fkey|check|idx>; PG 18 da NOT NULL ham nomli cheklov.
  • IF NOT EXISTS tuzilmani solishtirmaydi; schema — jadvallar "papkasi", search_path — qidirish tartibi.

Keyingi dars: Primary key tanlovi — serial, IDENTITY, UUID v4 va v7: qaysi ID qachon va o'lchovlar bilan.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
CREATE TABLE va cheklovlar: qoidalarni bazaning o'ziga yozish — IlmHamroh