12 — JOIN — jadvallarni bog'lash¶
⬅️ Oldingi: 11 — Aggregate funksiyalar va GROUP BY · 🏠 README · Keyingi: 13 — UNION — natijalarni birlashtirish ➡️
Bu bobda: ma'lumotni bir necha jadvalga bo'lib saqlaganimizdan keyin ularni qaytadan "yig'ishni" — JOIN'ni o'rganamiz:
ONsharti qatorlarni qanday ulashini, INNER va LEFT JOIN farqini, "hech narsa qilmaganlarni topish" (anti-join) qolipini, LEFT JOIN'ni buzib qo'yadigan WHERE tuzog'ini, 3-4 jadvalli zanjirlarni va JOIN + GROUP BY bilan real hisobotlar tuzishni ko'rib chiqamiz.
Muammo¶
kitoblarda muallif_id = 1 turibdi. "1" kim? Buni bilish uchun mualliflarga qarash kerak. 3-bobda ma'lumotni ataylab shunday bo'lib saqlagan edik: muallif haqidagi hamma narsa bitta joyda turadi, kitob esa unga faqat raqam orqali "ishora qiladi". Endi teskari amalni o'rganamiz — bo'lingan ma'lumotni qaytadan yig'ish. JOIN — ikki jadvalni yonma-yon qo'yish:
USE kutubxona;
SELECT kitoblar.nomi, mualliflar.ism
FROM kitoblar
JOIN mualliflar ON kitoblar.muallif_id = mualliflar.id;
O'qilishi: "kitoblar jadvalini ol, unga mualliflar'ni ULA, ulash sharti: kitobdagi muallif_id = muallifdagi id".
| nomi | ism |
|---|---|
| Otkan kunlar | Abdulla Qodiriy |
| Mehrobdan chayon | Abdulla Qodiriy |
| Sariq devni minib | Xudoyberdi Toxtaboyev |
| ... (jami 10 qator) | ... |
MySQL buni qanday qiladi? kitoblarning har bir qatorini oladi, mualliflardan ON shartiga mos qatorni topadi va ikkalasini bitta uzun qatorga "yelimlaydi". Mos qator topilmasa, oddiy JOIN'da bu qator natijaga umuman tushmaydi — birozdan keyin bu juda muhim bo'ladi.
Alias bilan qisqaroq (standart uslub):
📌 Ikkala jadvalda bir xil nomli ustun bo'lsa (bizda ikkalasida ham id bor), uni "yalang'och" yozib bo'lmaydi: SELECT id, nomi ... → Error: Column 'id' in field list is ambiguous — "qaysi id'ni nazarda tutdingiz?". Shuning uchun JOIN'da ustunlarni doim alias bilan aniq yozish odat tusiga kiradi: k.id, m.id.
INNER JOIN vs LEFT JOIN — asosiy farq¶
Tasavvur: ikki ro'yxatni solishtirayapsiz.
INNER JOIN (yoki shunchaki JOIN — bu ikkisi bir xil narsa) — faqat ikkala tomonda ham borlari:
-- Faqat kitob olgan a'zolar ko'rinadi:
SELECT a.ism, i.olingan_sana
FROM azolar a
INNER JOIN ijaralar i ON i.azo_id = a.id;
📌 JOIN qatorlar sonini o'zgartirishi mumkin: Aziz 2 ta kitob olgan, shuning uchun natijada Aziz 2 qatorda chiqadi. Bu xato emas — har bir ijara alohida voqea.
LEFT JOIN — chap jadval to'liq, o'ngdan mosi bo'lmasa NULL:
-- HAMMA a'zo ko'rinadi, kitob olmaganlarda sana NULL:
SELECT a.ism, i.olingan_sana
FROM azolar a
LEFT JOIN ijaralar i ON i.azo_id = a.id;
💡 Bizning ma'lumotlarda 6 a'zoning hammasi kamida bitta kitob olgan, shuning uchun hozircha ikkala query bir xil natija beradi. Farqni o'z ko'zingiz bilan ko'rish uchun kitob olmagan yangi a'zo qo'shing:
Endi ikkala queryni qayta ishlating: INNER JOIN'da Sevara umuman yo'q, LEFT JOIN'da esa bor — olingan_sana o'rnida NULL. Bu qatorni o'chirmang: bob davomida va masalalarda asqotadi.
📌 RIGHT JOIN ham bor — LEFT'ning aksi: o'ng jadval to'liq saqlanadi. Amalda juda kam ishlatiladi, chunki jadvallarning o'rnini almashtirib LEFT JOIN yozsangiz, xuddi shu natija chiqadi-yu, query "asosiy jadval — FROM'da turadi" qoidasi bilan osonroq o'qiladi.
Oltin pattern: "hech narsa qilmaganlarni topish"¶
-- Hech qachon kitob olmagan a'zolar:
SELECT a.*
FROM azolar a
LEFT JOIN ijaralar i ON i.azo_id = a.id
WHERE i.id IS NULL;
Mantiq: LEFT JOIN hammaga joy beradi; ijarasi yo'qlarda ijara ustunlari NULL; biz NULL'larni ushlaymiz. Bu pattern intervyularda doim so'raladi!
📌 Nega aynan i.id? Chunki id — ijaralarning PRIMARY KEY'i, jadvalning o'zida u hech qachon NULL bo'lmaydi. Demak, natijada i.id NULL bo'lsa, bu faqat bitta narsani anglatadi: LEFT JOIN bu a'zoga mos ijara topa olmagan. (i.azo_id ham bo'laverardi — muhimi, NOT NULL ustun tanlash.)
💡 Sevara'ni qo'shgan bo'lsangiz, bu query uni topib beradi. Qo'shmagan bo'lsangiz, natija bo'sh chiqadi — bu xato emas: "hech kim topilmadi" ham javob.
Tuzoq: LEFT JOIN + WHERE = yashirin INNER JOIN¶
LEFT JOIN yozdingizmi — o'ng jadval ustuniga WHERE'da shart qo'yishdan ehtiyot bo'ling:
-- Maqsad: HAMMA a'zo + faqat iyun ijaralari. Lekin...
SELECT a.ism, i.olingan_sana
FROM azolar a
LEFT JOIN ijaralar i ON i.azo_id = a.id
WHERE i.olingan_sana >= '2026-06-01'; -- ❌ kitob olmaganlar yo'qolib qoldi!
Nega? 11-bobdagi bajarilish tartibini eslang: WHERE JOIN'dan keyin ishlaydi. Kitob olmaganlarda i.olingan_sana NULL, NULL bilan taqqoslash esa hech qachon ROST bo'lmaydi (8-bob) — shu qatorlar filtrdan o'tolmaydi va LEFT JOIN amalda INNER JOIN'ga aylanib qoladi. Yechim — shartni ON ichiga ko'chirish:
SELECT a.ism, i.olingan_sana
FROM azolar a
LEFT JOIN ijaralar i ON i.azo_id = a.id
AND i.olingan_sana >= '2026-06-01'; -- ✅ hamma a'zo joyida
Qoida: o'ng jadvalga oid shart — ON ichiga, chap jadvalga oid shart — WHEREga. (Yagona istisno — yuqoridagi anti-join: u yerda IS NULL ataylab WHEREda turadi.)
3 va undan ko'p jadval¶
"Kim qaysi kitobni qachon olgan?" — bu savolga bitta jadval javob bera olmaydi: ismlar azolarda, nomlar kitoblarda, sanalar ijaralarda. E'tibor bering: ijaralar — "ko'prik" jadval, ikkala FK ham unda turibdi. Shuning uchun FROM'ni undan boshlash qulay:
-- Kim qaysi kitobni qachon olgan (3 jadval):
SELECT a.ism AS azo, k.nomi AS kitob, i.olingan_sana
FROM ijaralar i
JOIN azolar a ON a.id = i.azo_id
JOIN kitoblar k ON k.id = i.kitob_id;
-- 4 jadval — muallifgacha:
SELECT a.ism AS azo, k.nomi AS kitob, m.ism AS muallif
FROM ijaralar i
JOIN azolar a ON a.id = i.azo_id
JOIN kitoblar k ON k.id = i.kitob_id
JOIN mualliflar m ON m.id = k.muallif_id;
Qoida oddiy: har yangi jadval o'zining JOIN ... ON ... qatori bilan keladi va zanjirga bitta FK orqali ulanadi. 10 ta jadval bo'lsa ham mantiq o'zgarmaydi.
JOIN + GROUP BY = real hisobotlar¶
11-bobdagi GROUP BY'ga JOIN'ni qo'shsak, ish suhbatlaridagi klassik savol chiqadi: "har bir muallif nechta kitob yozgan — id emas, ismi bilan":
-- Har muallifning kitoblari soni (ismi bilan!):
SELECT m.ism, COUNT(k.id) AS kitoblar_soni
FROM mualliflar m
LEFT JOIN kitoblar k ON k.muallif_id = m.id
GROUP BY m.id, m.ism;
Nega LEFT? Kitobi yo'q muallif ham ro'yxatda 0 bilan ko'rinsin. Hozirgi ma'lumotlarda hamma muallifning kitobi bor — 0 ni ko'rish uchun bitta kitobsiz muallif qo'shing (buni ham o'chirmang, masalalarda kerak bo'ladi):
| ism | kitoblar_soni |
|---|---|
| Abdulla Qodiriy | 2 |
| Xudoyberdi Toxtaboyev | 2 |
| ... | ... |
| Oybek | 0 |
COUNT(k.id) — NULL'larni sanamaydi, shuning uchun Oybek'da 0 chiqadi. COUNT(*) bo'lsa 1 chiqardi — tuzoq! Chunki LEFT JOIN Oybek uchun ham bitta qator yaratadi (kitob ustunlari NULL bo'lsa ham), COUNT(*) esa qatorning o'zini sanayveradi.
📌 Nega GROUP BY m.id, m.ism — ikkalasi ham? MySQL 8 da ONLY_FULL_GROUP_BY rejimi default yoqilgan: SELECT'dagi har bir "oddiy" ustun GROUP BY'da bo'lishi (yoki undagi ustunga funksional bog'liq bo'lishi) shart. m.id PRIMARY KEY bo'lgani uchun MySQL 8 GROUP BY m.idning o'ziga ham ruxsat beradi, lekin ikkalasini yozish — boshqa SQL tizimlarida ham ishlaydigan eng xavfsiz uslub. Faqat GROUP BY m.ism deb yozish esa xavfli: ikki muallifning ismi bir xil bo'lsa, ular bitta qatorga qo'shilib ketadi — id bo'yicha guruhlash bundan saqlaydi.
12-bob masalalari¶
💡 5-, 7- va 8-masalalar uchun yuqorida qo'shgan Sevara va Oybek qatorlari asqotadi.
- (kutubxona) Kitob nomi + muallif ismi
- (kutubxona) Kitob nomi + muallif ismi + muallif davlati, faqat o'zbek mualliflar
- (kutubxona) Kim qaysi kitobni olgan (a'zo ismi + kitob nomi)
- (kutubxona) Hozir qaytarilmagan kitoblar: kitob nomi + kimda + qachondan beri
- (kutubxona) Hech qachon kitob olmagan a'zolar (anti-join!)
- (kutubxona) Hech kim olmagan kitoblar
- (kutubxona) Har muallif nechta kitob yozgan (ismi bilan, kitobsizlar 0 bilan)
- (kutubxona) Har a'zo nechta kitob olgan (ismi bilan, olmaganlar 0 bilan)
- (kutubxona) Har kitob necha marta ijaraga berilgan, eng mashhuridan boshlab
- (kutubxona) 4 jadvalli query: a'zo + kitob + muallif + olingan sana
- (dokon) Mahsulot nomi + kategoriya nomi
- (dokon) Buyurtmalar: mijoz ismi + sana + holat
- (dokon) Buyurtma tarkibi: buyurtma id + mahsulot nomi + soni + narx (buyurtma_qatorlari JOIN mahsulotlar)
- (dokon) Hech narsa buyurtma qilmagan mijozlar
- (dokon) Hech qachon sotilmagan mahsulotlar (buyurtma_qatorlari'da yo'qlar)
- (dokon) Har mijoz jami qancha pulga xarid qilgan (3 jadval + GROUP BY): mijozlar → buyurtmalar → buyurtma_qatorlari, SUM(narx*soni); 'bekor' buyurtmalarni chiqarib tashlang!
- (klinika) Qabullar: bemor ismi + shifokor ismi + sana + tashxis
- (klinika) Har shifokor (ismi bilan) nechta bemor ko'rgan va qancha yig'gan; birorta qabul qilmaganlar ham 0 bilan ko'rinsin
- (taksi) Har haydovchi + mashinasi modeli + raqami; mashinasiz haydovchi bormi — LEFT JOIN bilan tekshiring
- (taksi) Har haydovchi (ismi bilan): safarlar soni, jami daromad, o'rtacha baho — daromad bo'yicha kamayish tartibida