18 β ALTER, Constraint, Foreign Key¶
β¬ οΈ Oldingi: 17 β UPDATE va DELETE β chuqur Β· π README Β· Keyingi: 19 β Tranzaksiyalar β‘οΈ
Bu bobda: tayyor jadvalni buzmasdan o'zgartirishni (ALTER TABLE: ustun qo'shish, ta'rifini o'zgartirish, o'chirish), bazaning o'zi qo'riqlaydigan "qoidalar" β constraint'larni (NOT NULL, UNIQUE, DEFAULT, CHECK) va jadvallar orasidagi bog'lanish kafolati bo'lgan FOREIGN KEY'ni o'rganamiz; ota qator o'chirilganda nima bo'lishini ON DELETE (RESTRICT, CASCADE, SET NULL) bilan boshqarishni ham ko'ramiz.
Real loyihada jadval bir marta yaratilib, abadiy shu holicha qolmaydi: bugun mijozga email ustuni kerak bo'lib qoladi, ertaga telefon majburiy bo'lishi kerak, indinga bir ustun umuman ortiqcha chiqadi. Yaxshi yangilik β buning uchun jadvalni DROP qilib, ichidagi ma'lumot bilan birga yo'qotib, qaytadan qurish shart emas. Uyni buzib tashlamasdan ta'mirlaganday, jadvalni ham ishlab turgan holida o'zgartirish mumkin. Buning asbobi β ALTER TABLE.
ALTER β tayyor jadvalni o'zgartirish¶
ALTER TABLE mijozlar ADD COLUMN email VARCHAR(150) NULL; -- ustun qo'shish
ALTER TABLE mijozlar ADD COLUMN yosh INT AFTER ism; -- ma'lum joyga qo'shish
ALTER TABLE mijozlar MODIFY COLUMN telefon VARCHAR(25) NOT NULL; -- ta'rifini o'zgartirish
ALTER TABLE mijozlar RENAME COLUMN yosh TO yoshi; -- ustun nomini o'zgartirish
ALTER TABLE mijozlar DROP COLUMN yoshi; -- ustunni o'chirish
ALTER TABLE mijozlar RENAME TO xaridorlar; -- jadval nomini o'zgartirish
β οΈ Bu misollar dokon.mijozlar jadvalini haqiqatan o'zgartiradi β ayniqsa oxirgi RENAME TO xaridorlar jadval nomini o'zgartirib yuboradi, shundan keyin mijozlarga tayanadigan masalalar (shu bobdagi 11-masala ham!) va keyingi boblar ishlamay qoladi. Eng yaxshisi β bularni sinov bazasidagi alohida jadvalda mashq qiling. dokonda sinab ko'rgan bo'lsangiz, oxirida hammasini asl holiga qaytarib qo'ying:
ALTER TABLE xaridorlar RENAME TO mijozlar; -- nomni qaytarish
ALTER TABLE mijozlar DROP COLUMN email; -- qo'shilgan ustunni olib tashlash
ALTER TABLE mijozlar MODIFY COLUMN telefon VARCHAR(20); -- asl ta'rifiga qaytarish
π MODIFY ustunning butun ta'rifini qaytadan yozadi. Bu eng ko'p qoqiladigan joy: telefon avval VARCHAR(20) NOT NULL bo'lsa-yu, siz MODIFY COLUMN telefon VARCHAR(25) deb yozsangiz (NOT NULL'ni unutib), ustun endi NULL'ga ruxsat beradigan bo'lib qoladi. Shuning uchun MODIFY ishlatganda saqlab qolmoqchi bo'lgan hamma xususiyatni qayta sanab o'ting.
β οΈ Ehtiyot bo'ladigan ikki holat: DROP COLUMN ustun bilan birga undagi barcha ma'lumotni qaytarib bo'lmas tarzda o'chiradi; turini "toraytirish" esa (masalan, VARCHAR(150) β VARCHAR(20)) ichida uzunroq qiymat bor bo'lsa xato beradi. Va yana: millionlab qatorli jadvalda ALTER bir necha soniya, hatto daqiqa olishi mumkin β bu normal, MySQL ba'zi o'zgarishlarda jadvalni qayta quradi.
O'zgartirgandan keyin natijani tekshirib qo'yish odat bo'lsin:
DESCRIBE mijozlar; -- ustunlar ro'yxati: turi, NULL/NOT NULL, default
SHOW CREATE TABLE mijozlar; -- jadvalning to'liq ta'rifi (constraint'lar bilan)
Constraint β bazaning "qoidalari"¶
Constraint (cheklov) = "bu jadvalga noto'g'ri ma'lumot KIRMAYDI" kafolati. Buni binoning kirishidagi qo'riqchi deb tasavvur qiling: ruxsatnomasi yo'q odamni ichkariga qo'ymaydi. Tekshiruvni dasturga emas, bazaning o'ziga topshirishning katta afzalligi bor: dastur kodida tekshirishni unutib qo'yish mumkin (yoki bazaga uchta turli dastur ulanadi-yu, bittasida tekshiruv yo'q), baza esa hech qachon "unutmaydi" β qoida hamma uchun, doim ishlaydi:
| Constraint | Vazifasi |
|---|---|
NOT NULL |
bo'sh bo'lmasin |
UNIQUE |
takrorlanmasin |
PRIMARY KEY |
NOT NULL + UNIQUE + asosiy identifikator |
DEFAULT |
berilmasa, shu qiymat |
CHECK |
shart bajarilsin (MySQL 8.0.16+) |
FOREIGN KEY |
boshqa jadvaldagi mavjud qiymatga ishora qilsin |
ALTER TABLE klinika.shifokorlar ADD CONSTRAINT chk_tajriba CHECK (tajriba_yil >= 0);
-- Endi tajriba_yil = -5 kiritib BO'LMAYDI
ALTER TABLE klinika.bemorlar ADD CONSTRAINT uq_telefon UNIQUE (telefon);
-- Endi bitta telefon raqami ikki marta kirmaydi
π Constraint'ga nom berish odati: chk_ (CHECK), uq_ (UNIQUE), fk_ (FOREIGN KEY) prefikslari bilan. Nom bersangiz, qoida buzilganda xato xabarida aynan qaysi constraint "ushlab qolgani" ko'rinadi, keyinchalik o'chirish ham oson bo'ladi:
ALTER TABLE klinika.shifokorlar DROP CHECK chk_tajriba; -- CHECK'ni o'chirish
ALTER TABLE klinika.bemorlar DROP INDEX uq_telefon; -- UNIQUE aslida indeks, shunday o'chiriladi
β οΈ Tarixiy tuzoq: MySQL 8.0.16 dan oldingi versiyalar (jumladan, eski 5.7) CHECK'ni xatosiz qabul qilardi, lekin tekshirmasdi β qoida bordek ko'rinib, aslida ishlamasdi. MySQL 8 ning yangi versiyalarida bu tuzatilgan: CHECK haqiqatan qo'riqlaydi. Versiyangizni SELECT VERSION(); bilan tekshirib oling.
Yana bir muhim nuans: CHECK NULL'ni tekshirmaydi. CHECK (baho BETWEEN 1 AND 5) qoidasi bor jadvalga baho = NULL bemalol kiradi, chunki NULL bilan solishtirish natijasi "noma'lum" β rad etish uchun yetarli emas. NULL ham kirmasin desangiz, ustunga alohida NOT NULL qo'ying.
FOREIGN KEY β bog'lanish kafolati¶
Hozir kitoblar.muallif_idga 999 yozsangiz β MySQL indamaydi, garchi 999-muallif yo'q bo'lsa ham. Bu "yetim qator" (orphan row) β hisobotlarda yo'qolib qoladigan, JOIN'da chiqmay qoladigan, xatolar manbai bo'lgan ma'lumot. FOREIGN KEY shundan himoya qiladi: "bola" jadvaldagi ishora "ota" jadvalda haqiqatan mavjudligini har safar tekshiradi:
USE kutubxona;
ALTER TABLE kitoblar
ADD CONSTRAINT fk_kitob_muallif
FOREIGN KEY (muallif_id) REFERENCES mualliflar(id);
-- Endi:
INSERT INTO kitoblar (nomi, muallif_id) VALUES ('Test', 999);
-- β Xato: Cannot add or update a child row β 999-muallif yo'q!
DELETE FROM mualliflar WHERE id = 1;
-- β Xato: kitoblari bor muallifni o'chirib bo'lmaydi!
E'tibor bering: FK himoyasi ikki tomonlama ishlaydi. Bola jadvalga mavjud bo'lmagan ishorani kiritib bo'lmaydi, ota jadvaldan esa "bolalari" bor qatorni (default holatda) o'chirib bo'lmaydi.
FK qo'shishda ikkita texnik talab bor:
- Turlar mos bo'lsin:
muallif_id INTbo'lsa,mualliflar.idham INT bo'lishi kerak (ikkalasi ham signed yoki ikkalasi ham UNSIGNED). - Mavjud ma'lumot toza bo'lsin: jadvalda allaqachon yetim qatorlar bo'lsa (masalan,
muallif_id = 999), MySQL FK qo'shishga ruxsat bermaydi β avval ularni tuzating (UPDATE bilan to'g'rilang yoki NULL qiling).
π MySQL FK ustuniga avtomatik indeks ham qurib qo'yadi β bu tekshiruv tez ishlashi uchun kerak (indekslarni 21-bobda chuqur ko'ramiz). FK'ni o'chirish: ALTER TABLE kitoblar DROP FOREIGN KEY fk_kitob_muallif;.
ON DELETE β parent o'chsa nima bo'lsin?¶
FK qo'shayotganda "ota qator o'chirilsa, bolalari bilan nima qilamiz?" degan savolga oldindan javob berib qo'yish mumkin. Uchta variantdan bittasini tanlaysiz. Diqqat: fk_kitob_muallif nomli FK'ni yuqoridagi bo'limda allaqachon yaratgan edik β xuddi shu nom bilan ikkinchisini qo'shib bo'lmaydi (MySQL Duplicate foreign key constraint name xatosini beradi). Shuning uchun qaysi variantni sinamang, avval eskisini o'chiring:
-- Avval oldingi bo'limda yaratilgan FK'ni o'chiramiz (aks holda
-- "Duplicate foreign key constraint name" xatosi chiqadi):
ALTER TABLE kitoblar DROP FOREIGN KEY fk_kitob_muallif;
-- Variant 1: o'chirishga ruxsat YO'Q (default, eng xavfsiz)
ALTER TABLE kitoblar ADD CONSTRAINT fk_kitob_muallif
FOREIGN KEY (muallif_id) REFERENCES mualliflar(id) ON DELETE RESTRICT;
-- Variant 2: muallif o'chsa, kitoblari HAM o'chadi
ALTER TABLE kitoblar ADD CONSTRAINT fk_kitob_muallif
FOREIGN KEY (muallif_id) REFERENCES mualliflar(id) ON DELETE CASCADE;
-- Variant 3: muallif o'chsa, kitobda muallif_id = NULL bo'lib qoladi
ALTER TABLE kitoblar ADD CONSTRAINT fk_kitob_muallif
FOREIGN KEY (muallif_id) REFERENCES mualliflar(id) ON DELETE SET NULL;
Qachon qaysi? Pul/tarix bor joyda β RESTRICT. Komment/layk kabi "arzon" bog'liqlarda β CASCADE. "Bola qator qolsin, lekin egasi noma'lum bo'lsin" β SET NULL.
β οΈ CASCADE bilan ehtiyot bo'ling: u zanjir bo'ylab ishlaydi. Muallifni o'chirsangiz kitoblari o'chadi, kitoblarga CASCADE bilan bog'langan ijaralar bo'lsa β ular ham o'chadi. Bitta DELETE ko'zda tutilmagan minglab qatorni olib ketishi mumkin. SET NULL esa faqat ustun NULL'ga ruxsat bersagina ishlaydi β NOT NULL ustunga SET NULL qo'yib bo'lmaydi.
π ON DELETE'ning egizagi ham bor: ON UPDATE β ota qatorning id'si o'zgarsa nima bo'lishini belgilaydi. Amalda PK qiymatlari deyarli hech qachon o'zgartirilmaydi, shuning uchun ko'pincha yozilmaydi (default β RESTRICT).
18-bob masalalari¶
- (kutubxona)
kitoblargatil VARCHAR(30) DEFAULT 'ozbek'ustuni qo'shing - (kutubxona) Yangi kitob kiriting (tilsiz) β DEFAULT ishladimi?
- (kutubxona)
azolar.telefonga UNIQUE qo'shing:ALTER TABLE azolar ADD CONSTRAINT uq_telefon UNIQUE (telefon);β NULL'lar bor-ku, ishladimi? (UNIQUE bir nechta NULL'ga ruxsat beradi!) - (kutubxona) Mavjud telefon bilan a'zo kiritib ko'ring β endi xato chiqishi kerak
- (kutubxona)
kitoblarga FK qo'shing (yuqoridagi misol), 999-muallif bilan kitob kiritib ko'ring - (kutubxona)
ijaralarga 2 ta FK qo'shing: kitob_id β kitoblar, azo_id β azolar - (kutubxona) Kitobi bor muallifni DELETE qilib ko'ring β RESTRICT xatosini oling
- (kutubxona) CHECK qo'shing:
nusxa_soni >= 0. Keyin -3 kiritib ko'ring - (kutubxona) CHECK qo'shing:
yil BETWEEN 800 AND 2100. 2500-yil bilan sinab ko'ring - (dokon)
mahsulotlar.kategoriya_idga FK qo'shing (kategoriyalar'ga) - (dokon)
buyurtmalar.mijoz_idvabuyurtma_qatorlarining 2 ustuniga FK qo'shing - (dokon) CHECK:
narx > 0vasoni >= 0β ikkalasini qo'shib, buzib ko'ring - (dokon) Yangi jadval
izohlaryarating: id, mahsulot_id FK ON DELETE CASCADE bilan, matn. Buyurtmalarda mavjud mahsulotni tanlang (masalan, id = 1), unga izoh kiriting, keyin mahsulotni o'chirib ko'ring β o'chira olmaysiz (buyurtma_qatorlari RESTRICT ushlab turadi) β ana shu zanjirli himoyani his qiling. (Diqqat: 6- va 11-mahsulotlar hech bir buyurtmada yo'q β ulardan birini tanlasangiz, RESTRICT aralashmaydi, mahsulot izohi bilan birga o'chib ketadi) - (klinika)
qabullarga 2 ta FK (bemor, shifokor) RESTRICT bilan - (klinika) CHECK:
tolov >= 0;tajriba_yil <= 70 - (taksi)
mashinalar.haydovchi_idga FK ON DELETE SET NULL bilan (ustun NULL'ga ruxsat berishini tekshiring β kerak bo'lsa MODIFY qiling). Test haydovchi yarating, mashina bering, haydovchini o'chiring β mashinada nima qoldi? - (taksi) CHECK:
baho BETWEEN 1 AND 5(NULL ruxsat etiladi β CHECK NULL'ni tekshirmaydi, bu normal) - (sinov) Ataylab: bir-biriga FK bilan bog'langan 2 jadval yaratib, parent'ni DROP qilib ko'ring β nima dedi? (avval FK yoki child o'chiriladi)
- (kutubxona)
azolar.telefonni NOT NULL qiling (MODIFY) β NULL qiymat bor-ku (Nilufar Rahimovaning telefoni NULL), nima bo'ladi? Xatoni o'z ko'zingiz bilan ko'ring, keyin UPDATE bilan to'ldirib, qayta urinib ko'ring - Hammasiga FK qo'yib chiqqaningizdan keyin:
SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA='dokon' AND REFERENCED_TABLE_NAME IS NOT NULL;β bazadagi barcha FK'larni ko'ring