5-bo‘lim
Ko'p-ga-ko'p bog'lanish
Bog'lovchi jadval, kompozit kalit, bog'lanishning o'z atributlari va uch tomonlama bog'lanishlar.
Ushbu bo‘lim mundarijasi
Ikki jadval o'zaro ko'p bog'langanda, ular orasiga uchinchi jadval qo'yiladi. Buni chetlab o'tishga urinish - keng tarqalgan xato.
Nima uchun to'g'ridan-to'g'ri bog'lab bo'lmaydi #
Bog'lovchi jadval #
CREATE TABLE talabalar (
id INT PRIMARY KEY AUTO_INCREMENT,
ism VARCHAR(100) NOT NULL
);
CREATE TABLE fanlar (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(50) NOT NULL UNIQUE,
kredit TINYINT NOT NULL
);
CREATE TABLE yozilishlar (
talaba_id INT NOT NULL,
fan_id INT NOT NULL,
PRIMARY KEY (talaba_id, fan_id),
FOREIGN KEY (talaba_id) REFERENCES talabalar(id) ON DELETE CASCADE,
FOREIGN KEY (fan_id) REFERENCES fanlar(id) ON DELETE CASCADE
);
INSERT INTO talabalar (ism) VALUES ('Husanboy'), ('Malika'), ('Kamola');
INSERT INTO fanlar (nom, kredit) VALUES
('Matematika', 6), ('Fizika', 4), ('Ingliz tili', 3);
INSERT INTO yozilishlar (talaba_id, fan_id) VALUES
(1, 1), (1, 2), (1, 3),
(2, 1), (2, 3),
(3, 2);
SELECT t.ism, GROUP_CONCAT(f.nom ORDER BY f.nom SEPARATOR ', ') AS fanlar
FROM talabalar t
JOIN yozilishlar y ON y.talaba_id = t.id
JOIN fanlar f ON f.id = y.fan_id
GROUP BY t.id, t.ism
ORDER BY t.id;
+----------+---------------------------------+
| ism | fanlar |
+----------+---------------------------------+
| Husanboy | Fizika, Ingliz tili, Matematika |
| Malika | Ingliz tili, Matematika |
| Kamola | Fizika |
+----------+---------------------------------+
Teskari tomondan #
SELECT f.nom AS fan, COUNT(y.talaba_id) AS talabalar_soni
FROM fanlar f
LEFT JOIN yozilishlar y ON y.fan_id = f.id
GROUP BY f.id, f.nom
ORDER BY talabalar_soni DESC, f.nom;
+-------------+----------------+
| fan | talabalar_soni |
+-------------+----------------+
| Fizika | 2 |
| Ingliz tili | 2 |
| Matematika | 2 |
+-------------+----------------+
PRIMARY KEY (talaba_id, fan_id)
Bu bitta qatorda ikki ish qiladi:
- Har juftlik bir marta yoziladi - talaba bir fanga ikki marta yozila olmaydi;
- Indeks yaratadi -
talaba_idbo'yicha qidiruv tez.
Takrorlashga urinib ko'ring:
INSERT INTO yozilishlar VALUES (1, 1);
-- ERROR 1062: Duplicate entry '1-1' for key 'PRIMARY'
Bu qoida kodda emas, sxemada turibdi - hech qanday dastur uni buza olmaydi.
Ustunlar tartibi indeks uchun muhim: (talaba_id, fan_id)
indeksi talaba_id bo'yicha qidiruvni tezlashtiradi, lekin
faqat fan_id bo'yicha qidiruvni tezlashtirmaydi.
Ikkala yo'nalish ham kerak bo'lsa, ikkinchi indeks qo'shing:
CREATE INDEX idx_yozilishlar_fan ON yozilishlar (fan_id);
Buni 14 va 15-bo'limlarda batafsil ko'ramiz.
Bog'lanishning o'z atributlari #
Ko'pincha bog'lanishning o'z ma'lumoti bo'ladi:
CREATE TABLE yozilishlar_toliq (
talaba_id INT NOT NULL,
fan_id INT NOT NULL,
yozilgan DATE NOT NULL,
baho TINYINT NULL,
holat ENUM('faol', 'tugatgan', 'tashlagan') NOT NULL DEFAULT 'faol',
PRIMARY KEY (talaba_id, fan_id),
FOREIGN KEY (talaba_id) REFERENCES talabalar(id),
FOREIGN KEY (fan_id) REFERENCES fanlar(id)
);
INSERT INTO yozilishlar_toliq (talaba_id, fan_id, yozilgan, baho, holat) VALUES
(1, 1, '2026-09-01', 5, 'tugatgan'),
(1, 2, '2026-09-01', NULL, 'faol'),
(2, 1, '2026-09-02', 4, 'tugatgan'),
(3, 2, '2026-09-01', NULL, 'tashlagan');
SELECT t.ism, f.nom AS fan, y.holat, IFNULL(y.baho, 0) AS baho
FROM yozilishlar_toliq y
JOIN talabalar t ON t.id = y.talaba_id
JOIN fanlar f ON f.id = y.fan_id
ORDER BY t.id, f.id;
+----------+------------+-----------+------+
| ism | fan | holat | baho |
+----------+------------+-----------+------+
| Husanboy | Matematika | tugatgan | 5 |
| Husanboy | Fizika | faol | 0 |
| Malika | Matematika | tugatgan | 4 |
| Kamola | Fizika | tashlagan | 0 |
+----------+------------+-----------+------+
Boshida yozilishlar shunchaki ikki id edi. Atributlar
qo'shilgach, u o'z mavjudligiga aylandi:
| Boshida | Keyin |
|---|---|
| Bog'lanish | Mavjudlik |
(talaba_id, fan_id) | yozilgan, baho, holat |
| "Kim nimaga yozilgan" | "Yozilish" |
Bu tabiiy evolyutsiya. ER modelda buni assotsiativ mavjudlik deb atashadi.
Amaliy natija: agar bog'lovchi jadvalga boshqa jadval murojaat
qiladigan bo'lsa (masalan davomat jadvali yozilishga ishora
qilsa), unga sun'iy id qo'shing:
CREATE TABLE yozilishlar (
id INT PRIMARY KEY AUTO_INCREMENT,
talaba_id INT NOT NULL,
fan_id INT NOT NULL,
UNIQUE (talaba_id, fan_id),
...
);
UNIQUE qoidani saqlaydi, id esa murojaatni osonlashtiradi.
Vergul bilan ajratilgan ro'yxat - eng ko'p uchraydigan xato #
CREATE TABLE talabalar_yomon (
id INT PRIMARY KEY,
ism VARCHAR(100),
fanlar VARCHAR(255) -- '1,3,7' ko'rinishida
);
INSERT INTO talabalar_yomon VALUES
(1, 'Husanboy', '1,2,3'),
(2, 'Malika', '1,3'),
(3, 'Kamola', '2');
-- "Matematika ga yozilgan talabalarni top" - qanday yozamiz?
SELECT ism, fanlar FROM talabalar_yomon WHERE FIND_IN_SET('1', fanlar) > 0;
+----------+--------+
| ism | fanlar |
+----------+--------+
| Husanboy | 1,2,3 |
| Malika | 1,3 |
+----------+--------+
Yuqoridagi so'rov ishlaydi, lekin bu yechim emas:
| Muammo | Izoh |
|---|---|
| Indeks ishlamaydi | FIND_IN_SET butun jadvalni skanerlaydi |
| Tashqi kalit yo'q | '1,99,3' - 99-fan mavjud emas, hech kim sezmaydi |
JOIN qilib bo'lmaydi | Fan nomini olish uchun murakkab hiyla kerak |
| Yangilash qiyin | Bitta fanni olib tashlash - satrni qayta yig'ish |
| Atribut qo'shib bo'lmaydi | Baho yoki sanani qayerga yozamiz? |
| Uzunlik cheklovi | VARCHAR(255) to'lib qolishi mumkin |
| 1NF buzilgan | 6-bo'limda ko'ramiz |
Million qatorli jadvalda FIND_IN_SET bo'yicha qidiruv
soniyalar oladi, bog'lovchi jadvalda esa millisekundlar.
Xuddi shu narsa JSON massivi uchun ham to'g'ri:
fanlar JSON -- [1, 2, 3]
JSON ustun ba'zan o'rinli (5-bo'lim oxiridagi eslatma), lekin u ham tashqi kalit va oddiy indeksdan mahrum.
Qoida: ro'yxat kerak bo'lsa - jadval yarating.
To'g'ri yechim bilan taqqoslash #
-- Bog'lovchi jadval bilan xuddi shu savol
SELECT t.ism
FROM talabalar t
JOIN yozilishlar y ON y.talaba_id = t.id
WHERE y.fan_id = 1
ORDER BY t.id;
+----------+
| ism |
+----------+
| Husanboy |
| Malika |
+----------+
Bog'lovchi jadvalda kerakli indekslar avtomatik hosil bo'ladi:
SELECT INDEX_NAME AS indeks, SEQ_IN_INDEX AS tartib, COLUMN_NAME AS ustun
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'yozilishlar'
ORDER BY BINARY INDEX_NAME, SEQ_IN_INDEX;
+---------+--------+-----------+
| indeks | tartib | ustun |
+---------+--------+-----------+
| PRIMARY | 1 | talaba_id |
| PRIMARY | 2 | fan_id |
| fan_id | 1 | fan_id |
+---------+--------+-----------+
PRIMARY indeksi (talaba_id, fan_id) - biz uni yozgan edik.
Lekin fan_id uchun alohida indeks paydo bo'ldi - biz uni
so'ramagan edik.
Buni InnoDB o'zi qildi: har bir FOREIGN KEY uchun indeks
majburiy, aks holda tashqi kalitni tekshirish har safar
butun jadvalni skanerlashni talab qilardi.
Natijada bog'lovchi jadval ikkala yo'nalishda ham tez ishlaydi:
WHERE talaba_id = ?→PRIMARYindeksi;WHERE fan_id = ?→fan_idindeksi.
PRIMARY ning ikkinchi ustuni bo'lgani uchun fan_id bo'yicha
qidiruv yolg'iz PRIMARY bilan tezlashmasdi - shuning uchun
bu qo'shimcha indeks bejiz emas.
Indekslar qanday ishlashini 14-bo'limda, EXPLAIN bilan
tekshirishni 15-bo'limda ko'ramiz.
Uch tomonlama bog'lanish #
-- Talaba + fan + semestr: bir talaba bir fanni QAYTA o'qishi mumkin
CREATE TABLE semestrlar (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(20) NOT NULL UNIQUE
);
CREATE TABLE yozilishlar_semestr (
talaba_id INT NOT NULL,
fan_id INT NOT NULL,
semestr_id INT NOT NULL,
baho TINYINT NULL,
PRIMARY KEY (talaba_id, fan_id, semestr_id),
FOREIGN KEY (talaba_id) REFERENCES talabalar(id),
FOREIGN KEY (fan_id) REFERENCES fanlar(id),
FOREIGN KEY (semestr_id) REFERENCES semestrlar(id)
);
INSERT INTO semestrlar (nom) VALUES ('2026-kuz'), ('2027-bahor');
INSERT INTO yozilishlar_semestr VALUES
(1, 1, 1, 2), -- yiqildi
(1, 1, 2, 5); -- qayta o'qib o'tdi
SELECT t.ism, f.nom AS fan, s.nom AS semestr, y.baho
FROM yozilishlar_semestr y
JOIN talabalar t ON t.id = y.talaba_id
JOIN fanlar f ON f.id = y.fan_id
JOIN semestrlar s ON s.id = y.semestr_id
ORDER BY s.id;
+----------+------------+------------+------+
| ism | fan | semestr | baho |
+----------+------------+------------+------+
| Husanboy | Matematika | 2026-kuz | 2 |
| Husanboy | Matematika | 2027-bahor | 5 |
+----------+------------+------------+------+
E'tibor bering: (talaba_id, fan_id) kaliti bilan Husanboy
matematikani qayta o'qiy olmasdi.
semestr_id qo'shilgach, qoida o'zgardi:
| Kalit | Qoida |
|---|---|
(talaba_id, fan_id) | Fanni bir marta o'qish mumkin |
(talaba_id, fan_id, semestr_id) | Har semestrda bir marta |
Bu shunchaki texnik detal emas - bu biznes qarori. Uni buyurtmachi bilan aniqlashtiring:
"Talaba yiqilgan fanni qayta o'qiy oladimi?"
Javob "ha" bo'lsa, semestr kalitga kiradi. "Yo'q" bo'lsa - kirmaydi va sxema qayta o'qishni jismonan taqiqlaydi.
Bunday savollarni loyihalash paytida bering. Keyinchalik birlamchi kalitni o'zgartirish - eng qimmat migratsiyalardan biri.
- Talaba va fanni bog'lovchi jadval bilan bog'lang.
GROUP_CONCATbilan har talabaning fanlarini chiqaring.- Teskari tomondan - har fanning talabalarini sanang.
- Takrorlangan juftlik kiritishga urinib ko'ring.
- Bog'lovchi jadvalga
bahovaholatatributlarini qo'shing. - Vergulli ro'yxat bilan saqlab,
FIND_IN_SETbilan qidiring. - Vergulli ro'yxatning oltita kamchiligini sanang.
EXPLAINbilan ikki yechimni taqqoslang.- Uch tomonlama bog'lanish uchun jadval yarating.
- Kalitga ustun qo'shish qoidani qanday o'zgartirishini ayting.
Xulosa #
- N:M bog'lanish bog'lovchi jadval talab qiladi.
- Bitta N:M ikkita 1:N ga aylanadi.
- Kompozit birlamchi kalit takrorlanishni to'xtatadi.
- Indeks ustunlar tartibiga bog'liq - ikkinchi yo'nalish uchun qo'shimcha indeks kerak.
- Bog'lanishga atribut qo'shilsa, u mavjudlikka aylanadi.
- Unga boshqa jadval murojaat qilsa - sun'iy
idqo'shing. - Vergulli ro'yxat indeks, tashqi kalit va
JOINdan mahrum qiladi. - Ro'yxat kerak bo'lsa - jadval yarating.
- Kalitga ustun qo'shish biznes qoidasini o'zgartiradi.
Keyingi bo'limda normalizatsiyani - birinchi normal shakldan boshlaymiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.