CREATE TABLE — constraints, PRIMARY KEY, FOREIGN KEY, CHECK

~20 daq · PostgreSQL · DDL va DML

Foydalanuvchilar jadvalini yaratyapsiz. Quyidagi holatlar bo'lmasligi kerak:

  • Ikki foydalanuvchi bir xil email bilan ro'yxatdan o'tishi
  • Email bo'sh qolishi
  • Yosh manfiy son bo'lishi
  • Buyurtmada mavjud bo'lmagan foydalanuvchi ko'rsatilishi

Bularni ilovaning kodida tekshirish mumkin. Lekin bu yetarli emas — boshqa dasturchi yangi API yozsa va tekshiruvni unutsa, yoki ma'lumot to'g'ridan-to'g'ri database ga kiritilsa — noto'g'ri ma'lumot o'tib ketadi.

Bu qoidalar database darajasida belgilanishi kerak. Shunda qaysi dastur, qaysi til orqali kiritmasin — qoida buzilmaydi. Buning uchun constraint lar ishlatiladi.


Constraint nima?

Constraint — jadvalga qo'yiladigan qoida. Shu qoidaga uymagan ma'lumot database tomonidan rad etiladi, xato qaytariladi.

Hayotiy o'xshatma

Pasport olish uchun ariza to'ldirish. Ariza blankasida qoidalar bor: seriya raqami bo'lishi shart, har pasportning raqami noyob bo'lishi kerak, tug'ilgan sana bo'lishi kerak. Qoida buzilsa — ariza qabul qilinmaydi. Constraint lar — jadvalning "ariza blankas qoidalari".


Jadval yaratish

CREATE TABLE foydalanuvchilar (
    id         SERIAL PRIMARY KEY,
    ism        TEXT NOT NULL,
    email      TEXT NOT NULL UNIQUE,
    yosh       INTEGER CHECK (yosh >= 0),
    rol        TEXT DEFAULT 'user',
    yaratilgan TIMESTAMPTZ DEFAULT NOW()
);

Har bir qator: ustun nomi → tur → constraint. Endi har bir constraint ni alohida ko'rib chiqamiz.


PRIMARY KEY — har qatorning noyob belgisi

Jadvalda minglab qator bo'ladi. Ularning har birini qanday farqlaymiz? Buning uchun har qatorga noyob identifikator kerak — PRIMARY KEY.

PRIMARY KEY ikki qoidani birdan ta'minlaydi: bo'sh bo'lmaydi (NOT NULL) va takrorlanmaydi (UNIQUE). Har jadvalda faqat bitta PRIMARY KEY bo'lishi mumkin.

SERIAL — PRIMARY KEY bilan juft ishlatiladi. Yangi qator qo'shilganda qiymat o'zi oshib boradi: 1, 2, 3...

CREATE TABLE mahsulotlar (
    id   SERIAL PRIMARY KEY,  -- ← 1, 2, 3 ... o'zi oshadi
    nom  TEXT NOT NULL
);

Ba'zida bitta ustun yetarli emas — ikki ustun birgalikda noyob bo'lishi kerak. Masalan, bir buyurtmada bir mahsulot faqat bir marta bo'lishi kerak:

CREATE TABLE buyurtma_tarkibi (
    buyurtma_id  INTEGER,
    mahsulot_id  INTEGER,
    miqdor       INTEGER NOT NULL,
    PRIMARY KEY (buyurtma_id, mahsulot_id)  -- ← juft birgalikda noyob
);

NOT NULL — majburiy maydon

Ustundagi qiymat bo'sh (NULL) qolmasligi kerak bo'lganda NOT NULL yoziladi.

Qaysi maydonlar majburiy? Ism, email, telefon — bo'sh bo'lmaydi. Bio, manzil, rasm — ixtiyoriy, bo'sh qolishi mumkin.

CREATE TABLE foydalanuvchilar (
    id     SERIAL PRIMARY KEY,
    ism    TEXT NOT NULL,   -- ← majburiy
    email  TEXT NOT NULL,   -- ← majburiy
    bio    TEXT             -- ← ixtiyoriy, NULL bo'lishi mumkin
);

Email ko'rsatilmay foydalanuvchi qo'shilmoqchi bo'lsa — database rad etadi:

INSERT INTO foydalanuvchilar (ism) VALUES ('Vali');
-- ERROR: null value in column "email" violates not-null constraint

UNIQUE — takrorlanmasin

Bir xil email bilan ikki foydalanuvchi bo'lmasligi kerak. Buning uchun UNIQUE — bu ustundagi har bir qiymat jadvalda faqat bir marta bo'lishi mumkin.

CREATE TABLE foydalanuvchilar (
    id      SERIAL PRIMARY KEY,
    email   TEXT NOT NULL UNIQUE,  -- ← takrorlanmaydi
    telefon TEXT UNIQUE            -- ← ixtiyoriy, lekin kiritilsa noyob bo'lishi shart
);

Bir xil email ikkinchi marta kiritilsa:

INSERT INTO foydalanuvchilar (email) VALUES ('ali@mail.com');  -- ✓
INSERT INTO foydalanuvchilar (email) VALUES ('ali@mail.com');
-- ERROR: duplicate key value violates unique constraint

DEFAULT — standart qiymat

Har yangi foydalanuvchi ro'yxatdan o'tganda roli 'user' bo'ladi. Har safar bu qiymatni yozib o'tirmaslik uchun DEFAULT ishlatiladi — qiymat kiritilmasa, database o'zi standart qiymatni qo'yadi.

CREATE TABLE foydalanuvchilar (
    id         SERIAL PRIMARY KEY,
    ism        TEXT NOT NULL,
    rol        TEXT        DEFAULT 'user',    -- ← kiritilmasa 'user'
    faol       BOOLEAN     DEFAULT TRUE,      -- ← kiritilmasa true
    yaratilgan TIMESTAMPTZ DEFAULT NOW()      -- ← kiritilmasa hozirgi vaqt
);

Faqat ism kiritildi — qolganlari o'zi to'ldirildi:

INSERT INTO foydalanuvchilar (ism) VALUES ('Ali');

SELECT * FROM foydalanuvchilar WHERE ism = 'Ali';
--  id |  ism  |  rol  | faol |      yaratilgan
-- ----+-------+-------+------+----------------------
--   1 | Ali   | user  | t    | 2024-03-15 14:30:00

CHECK — o'z qoidangiz

Ba'zan standart constraintlar yetmaydi — o'zingiz qoida belgilashingiz kerak. Yosh manfiy bo'lmasligi, narx noldan katta bo'lishi, chegirma 0 dan 100 gacha bo'lishi. Buning uchun CHECK.

CREATE TABLE foydalanuvchilar (
    id    SERIAL PRIMARY KEY,
    ism   TEXT NOT NULL,
    yosh  INTEGER CHECK (yosh >= 0 AND yosh <= 150)
);
INSERT INTO foydalanuvchilar (ism, yosh) VALUES ('Ali', -5);
-- ERROR: new row violates check constraint "foydalanuvchilar_yosh_check"

Ikki ustunni birgalikda tekshirish ham mumkin. Masalan, aksiya tugash sanasi boshlanish sanasidan keyin bo'lishi kerak:

CREATE TABLE aksiyalar (
    id          SERIAL PRIMARY KEY,
    boshlanish  DATE NOT NULL,
    tugash      DATE NOT NULL,
    CHECK (tugash > boshlanish)  -- ← jadval darajasida, ikkala ustun birgalikda
);

FOREIGN KEY — jadvallar orasidagi bog'liq

Buyurtma biror foydalanuvchiga tegishli. Lekin database bu bog'liqni o'zi bilmaydi — aytib qo'ymasangiz, mavjud bo'lmagan foydalanuvchiga ham buyurtma qo'shilishi mumkin.

FOREIGN KEY — "bu ustundagi qiymat boshqa jadvaldagi ustunda mavjud bo'lishi shart" degan qoida. Bu ikki jadvalni bog'laydi va ma'lumotlar izchilligini ta'minlaydi.

CREATE TABLE foydalanuvchilar (
    id   SERIAL PRIMARY KEY,
    ism  TEXT NOT NULL
);

CREATE TABLE buyurtmalar (
    id               SERIAL PRIMARY KEY,
    foydalanuvchi_id INTEGER NOT NULL REFERENCES foydalanuvchilar(id),
    --                                ↑ foydalanuvchilar.id da bor bo'lishi shart
    miqdor           NUMERIC(10, 2) NOT NULL,
    sana             TIMESTAMPTZ DEFAULT NOW()
);

Mavjud bo'lmagan foydalanuvchiga buyurtma qo'shilmoqchi bo'lsa:

INSERT INTO buyurtmalar (foydalanuvchi_id, miqdor) VALUES (999, 50000);
-- ERROR: insert or update on table "buyurtmalar" violates foreign key constraint
-- Detail: Key (foydalanuvchi_id)=(999) is not present in table "foydalanuvchilar".

ON DELETE — ota jadval o'chirilganda nima bo'ladi?

Ali o'chirilmoqchi. Lekin uning buyurtmalari bor. Bu holda nima bo'lishi kerak? Uch xil strategiya mavjud — qaysi biri to'g'ri ekanligi loyihaga bog'liq:

RESTRICT — standart. Ali o'chirilmaydi, avval buyurtmalarini o'chirish kerak. Xavfsiz, lekin qo'lda tartib kerak.

CASCADE — Ali o'chirilsa, uning barcha buyurtmalari ham avtomatik o'chadi. Qulay, lekin ehtiyot bo'lish kerak — tasodifan o'chirib yubormaslik uchun.

SET NULL — Ali o'chirilsa, buyurtmalardagi foydalanuvchi_id NULL ga o'zgaradi. Buyurtmalar saqlanib qoladi, lekin kimnikiligi noma'lum bo'ladi. Arxiv maqsadida foydali.

CREATE TABLE buyurtmalar (
    id               SERIAL PRIMARY KEY,
    foydalanuvchi_id INTEGER REFERENCES foydalanuvchilar(id) ON DELETE CASCADE,
    miqdor           NUMERIC(10, 2) NOT NULL
);

psql da ko'rish

\l                    — databaselar ro'yxati
\c mydb               — databasega kirish
\dt                   — jadvallar ro'yxati
\d foydalanuvchilar   — jadval tuzilmasi (ustunlar, constraint lar)
\q                    — chiqish

Tez-tez uchraydigan xatolar

FOREIGN KEY uchun avval ota jadval yaratilishi kerak

buyurtmalar jadvali foydalanuvchilar ga murojaat qiladi. Lekin foydalanuvchilar hali yaratilmagan bo'lsa — xato chiqadi. Avval "ota" jadval yaratiladi:

-- ❌ foydalanuvchilar yo'q
CREATE TABLE buyurtmalar (
    foydalanuvchi_id INTEGER REFERENCES foydalanuvchilar(id)
);

-- ✅ avval ota jadval, keyin bola
CREATE TABLE foydalanuvchilar (id SERIAL PRIMARY KEY, ism TEXT);
CREATE TABLE buyurtmalar (foydalanuvchi_id INTEGER REFERENCES foydalanuvchilar(id));

FOREIGN KEY bor jadval o'chirilmaydi

foydalanuvchilar o'chirilmoqchi, lekin buyurtmalar unga bog'liq. Database "avval bog'liq narsalarni yo'q qiling" deydi:

DROP TABLE foydalanuvchilar;
-- ERROR: cannot drop table because other objects depend on it

-- ✅ avval bola jadval
DROP TABLE buyurtmalar;
DROP TABLE foydalanuvchilar;

NULL va UNIQUE birga ishlashi

UNIQUE constraint NULL qiymatlarni bir-biridan farqli deb hisoblaydi. Ya'ni bir nechta NULL bo'lishi mumkin — bu ba'zilarni ajablantiradi:

CREATE TABLE test (telefon TEXT UNIQUE);

INSERT INTO test VALUES (NULL);  -- ✓
INSERT INTO test VALUES (NULL);  -- ✓ ham — NULL lar "bir xil" hisoblanmaydi
INSERT INTO test VALUES ('+998901234567');  -- ✓
INSERT INTO test VALUES ('+998901234567');  -- ❌ qiymat takrorlandi

Xulosa

Constraint — database darajasidagi qoida. Buzilsa — ma'lumot qabul qilinmaydi.

PRIMARY KEY  — har qatorning noyob belgisi, bo'sh bo'lmaydi
NOT NULL     — majburiy maydon, bo'sh qolmaydi
UNIQUE       — takrorlanmaydi
DEFAULT      — qiymat kiritilmasa standart ishlatiladi
CHECK        — o'zingiz belgilagan qoida
FOREIGN KEY  — boshqa jadval bilan bog'liq, izchillikni ta'minlaydi

Constraint lar ilovadan mustaqil — qaysi dastur yoki til orqali kiritmasin, qoida buzilmaydi.


Keyingi dars

7-dars: ALTER TABLE, DROP — ustun qo'shish, o'zgartirish, o'chirish