Mundarija (39)
- Bu darsda
- 1. Nega bu kerak?
- 2. CREATE TABLE anatomiyasi
- 2.1 Eng oddiy ko'rinish
- 2.2 Kanon jadvali — vazifalar
- 3. NOT NULL va DEFAULT
- 3.1 NOT NULL
- 3.2 DEFAULT
- 4. PRIMARY KEY
- 4.1 Ikki qoida bitta so'zda
- 4.2 Identity — id ni baza beradi
- 5. UNIQUE
- 5.1 Takror email
- 5.2 UNIQUE va NULL
- 6. CHECK
- 6.1 Istalgan shart
- 6.2 Cheklovlarni ataylab buzamiz
- 6.3 JS trim() va btrim() farqi
- 7. Cheklovlarni nomlash
- 7.1 Nega nom kerak
- 7.2 PostgreSQL 18: NOT NULL ham nomli
- 8. IF NOT EXISTS va schema
- 8.1 IF NOT EXISTS tuzog'i
- 8.2 Schema — jadvallar "papkasi"
- 9. SQL qadami: 001-jadvallar.sql
- 10. Ko'p uchraydigan xatolar
- 10.1 Oxirgi ustundan keyin vergul
- 10.2 NOT NULL siz ustunlar
- 10.3 Registrga sezgir noyob email
- 10.4 Faqat frontend tekshiruvi
- 10.5 Nomsiz cheklovlar
- 11. Mashqlar
- 1-mashq (oson): cheklovni tanlang
- 2-mashq (o'rta): qaysi biri o'tadi?
- 3-mashq (qiyin): «Bahor» menyusi
- 4-mashq: SQL qadami
- 12. Real ishda
- Xulosa
- Manbalar
CREATE TABLE va cheklovlar: qoidalarni bazaning o'ziga yozish
Qisqacha:
CREATE TABLEjadvalni 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 TABLEbilan ustunlar, turlar va sukut qiymatlarni yozasiz.NOT NULL,PRIMARY KEY,UNIQUE,CHECKcheklovlarini 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 EXISTStuzog'ini va schema (namespace) nima ekanini bilasiz. vazifalarbazasining 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
curlbilan 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
UPDATEqiladi 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
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:
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)
);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:
INSERT INTO foydalanuvchilar (email) VALUES ('sardor@example.com');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):
INSERT INTO vazifalar (egasi_id, matn) VALUES (1, 'Non olish')
RETURNING *; id | egasi_id | matn | bajarildi | yaratilgan_vaqt
----+----------+-----------+-----------+-------------------------------
1 | 1 | Non olish | f | 2026-10-24 04:55:19.843836+00
(1 row)
INSERT 0 1id — 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:
INSERT INTO foydalanuvchilar (id, email, ism)
VALUES (100, 'sardor@example.com', 'Sardor');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:
CREATE UNIQUE INDEX foydalanuvchilar_email_key
ON foydalanuvchilar (lower(email));CREATE INDEXlower(email) — emailning kichik harfli ko'rinishi. Indeks shu ko'rinish bo'yicha noyoblikni tekshiradi. Tekshiruv:
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('aziz@example.com', 'Aziz Karimov');
INSERT INTO foydalanuvchilar (email, ism)
VALUES ('Aziz@Example.COM', 'Boshqa Aziz');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:
vazifalarjadvaligaUNIQUE (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:
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:
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));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:
INSERT INTO vazifalar (egasi_id, matn) VALUES (1, repeat('oʻ', 50));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 ustungaNULLyozilsa 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:
<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):
SELECT conname FROM pg_constraint
WHERE conrelid = 'vazifalar'::regclass
ORDER BY conname; 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:
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:
CREATE TABLE IF NOT EXISTS vazifalar (id int);NOTICE: relation "vazifalar" already exists, skipping
CREATE TABLETarjimasi: "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:
CREATE SCHEMA hisobot;
CREATE TABLE hisobot.vazifalar (kun date, soni int);Natija:
CREATE SCHEMA
CREATE TABLE\dt *.vazifalar (hamma schema'lardagi vazifalar):
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:
SHOW search_path; 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):
-- 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'limidagidekKanon 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:
psql -X -1 -v ON_ERROR_STOP=1 \
-d postgres://postgres@127.0.0.1:5499/vazifalar \
-f sql/001-jadvallar.sqlTekshiruv — \d vazifalar. Kanon chiqishi:
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:
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
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
- Ustun bo'sh bo'lmasin — .
- Qiymat berilmasa
falsebo'lsin — false. - Narx musbat bo'lsin — (narx > 0).
- Ikki qatorda bir xil telefon bo'lmasin — .
- 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?
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;:
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)- O'tdi. 2. Yo'q — registrga befarq takror. 3. Yo'q — emailda bo'shliq. 4. O'tdi,
bajarildi—DEFAULT false. 5. Yo'q — ochiqNULLDEFAULTni ishga tushirmaydi. 6. O'tdi — 77-foydalanuvchi yo'q, lekinegasi_idhali 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
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):
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 NULLyokiCHECKyozilmaganidan. - 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,checkbor — ular aynan shu SQL'ni yaratadi. Ba'zi cheklovlarni (ifoda ustidagi noyoblik, murakkabCHECK) ORM qo'llamaydi va qo'lda migratsiya yoziladi. - Intervyu savollari: "Validatsiya frontend'da bo'lsa, bazada nega kerak?", "
UNIQUEvaPRIMARY KEYfarqi?", "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 (ochiqNULLda emas);PRIMARY KEY= noyob + bo'sh emas + indeks.UNIQUEustun bo'yicha; registrga befarq email —CREATE UNIQUE INDEX … (lower(email)).CHECK— istalgan shart;NULLuni o'tadi, shuning uchunNOT NULLbilan birga;char_lengthbelgini sanaydi.- Cheklov nomlari —
<jadval>_<ustun>_<pkey|key|fkey|check|idx>; PG 18 daNOT NULLham nomli cheklov. IF NOT EXISTStuzilmani 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
- PostgreSQL 18 hujjati — CREATE TABLE: https://www.postgresql.org/docs/18/sql-createtable.html
- PostgreSQL 18 hujjati — Constraints: https://www.postgresql.org/docs/18/ddl-constraints.html
- PostgreSQL 18 hujjati — Schemas: https://www.postgresql.org/docs/18/ddl-schemas.html
- PostgreSQL 18 hujjati — Error Codes (23505, 23514): https://www.postgresql.org/docs/18/errcodes-appendix.html
- PostgreSQL 18 relizi eslatmalari: https://www.postgresql.org/docs/18/release-18.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!