11-bo‘lim
JOIN - jadvallarni birlashtirish
INNER, LEFT, RIGHT va CROSS JOIN, bir nechta jadvalni birlashtirish, o'ziga JOIN va JOIN + GROUP BY.
Ushbu bo‘lim mundarijasi
Ma'lumot bir nechta jadvalga bo'lingan. JOIN ularni birgalikda o'qish imkonini beradi.
INNER JOIN - eng ko'p ishlatiladigan #
SELECT
t.ism,
t.familiya,
g.nomi AS guruh
FROM talabalar t
INNER JOIN guruhlar g ON g.id = t.guruh_id;
+----------+----------+--------+
| ism | familiya | guruh |
+----------+----------+--------+
| Husanboy | Qodirov | IT-101 |
| Sardor | Aliyev | IT-101 |
| Aziza | Tosheva | IT-101 |
| Malika | Yusupova | IT-102 |
| Bekzod | Rasulov | IT-102 |
+----------+----------+--------+
INNER so'zini tushirib qoldirish mumkin - JOIN o'zi INNER JOIN degani.
JOIN turlari #
| Turi | Nima qaytaradi |
|---|---|
INNER JOIN | Faqat ikkala jadvalda mos kelgan qatorlar |
LEFT JOIN | Chap jadvaldagi barcha + mos kelgan o'ng |
RIGHT JOIN | O'ng jadvaldagi barcha + mos kelgan chap |
CROSS JOIN | Barcha kombinatsiyalar (dekart ko'paytmasi) |
LEFT JOIN - juda muhim #
-- Barcha guruhlar, hatto talabasi bo'lmagani ham
SELECT
g.nomi AS guruh,
t.ism,
t.familiya
FROM guruhlar g
LEFT JOIN talabalar t ON t.guruh_id = g.id;
+---------+----------+----------+
| guruh | ism | familiya |
+---------+----------+----------+
| IT-101 | Husanboy | Qodirov |
| IT-101 | Sardor | Aliyev |
| IT-102 | Malika | Yusupova |
| IT-201 | Nodira | Sobirova |
| DIZ-101 | NULL | NULL | <- talabasi yo'q
+---------+----------+----------+
INNER JOIN bo'lganda DIZ-101 umuman ko'rinmasdi.
LEFT JOIN qachon kerak?"Barcha X lar va ularning Y lari (agar bo'lsa)" degan savolga javob berish uchun:
- Barcha mahsulotlar va ularning sharhlari
- Barcha foydalanuvchilar va ularning oxirgi buyurtmasi
- Barcha kategoriyalar va ulardagi mahsulotlar soni
Agar "bo'lmasa ham ko'rsat" kerak bo'lsa - LEFT JOIN.
Bog'liq yozuvi yo'qlarni topish #
-- Talabasi bo'lmagan guruhlar
SELECT g.nomi
FROM guruhlar g
LEFT JOIN talabalar t ON t.guruh_id = g.id
WHERE t.id IS NULL;
+---------+
| nomi |
+---------+
| DIZ-101 |
+---------+
-- Hech qachon buyurtma bermagan mijozlar
SELECT m.ism, m.email
FROM mijozlar m
LEFT JOIN buyurtmalar b ON b.mijoz_id = m.id
WHERE b.id IS NULL;
LEFT JOIN ... WHERE o'ng_jadval.id IS NULL = "chap jadvalda bor, lekin
o'ng jadvalda yo'q". "Yetim yozuvlar", "faol bo'lmagan mijozlar",
"sotilmagan mahsulotlar" kabi savollarning standart yechimi.
ON va WHERE farqi #
LEFT JOIN da shartni to'g'ri joyga qo'ying-- 1: shart ON da - LEFT JOIN saqlanadi
SELECT g.nomi, COUNT(t.id) AS soni
FROM guruhlar g
LEFT JOIN talabalar t ON t.guruh_id = g.id AND t.faolmi = TRUE
GROUP BY g.id;
-- Natija: BARCHA guruhlar, faol talabalar soni bilan (0 bo'lsa ham)
-- 2: shart WHERE da - LEFT JOIN INNER JOIN ga aylanadi!
SELECT g.nomi, COUNT(t.id) AS soni
FROM guruhlar g
LEFT JOIN talabalar t ON t.guruh_id = g.id
WHERE t.faolmi = TRUE
GROUP BY g.id;
-- Natija: faqat faol talabasi BOR guruhlar
Sabab: WHERE JOIN dan keyin ishlaydi va NULL qatorlarni tashlab yuboradi.
Qoida: o'ng jadvalga tegishli shart ON da, chap jadvalga tegishlisi WHERE da.
Bir nechta jadvalni birlashtirish #
SELECT
m.ism AS mijoz,
b.id AS buyurtma,
mh.nomi AS mahsulot,
be.soni,
be.narx,
be.soni * be.narx AS jami,
k.nomi AS kategoriya
FROM buyurtmalar b
JOIN mijozlar m ON m.id = b.mijoz_id
JOIN buyurtma_elementlari be ON be.buyurtma_id = b.id
JOIN mahsulotlar mh ON mh.id = be.mahsulot_id
JOIN kategoriyalar k ON k.id = mh.kategoriya_id
WHERE b.holat = 'tolangan'
ORDER BY b.yaratilgan DESC;
- Har bir
JOINni yangi satrga ONshartlarini tekislang- Jadval taxalluslari qisqa va ma'noli bo'lsin
ONda har doimo'ng.id = chap.ustuntartibida yozing
Bu 5 jadvalli so'rovni ham o'qish mumkin qiladi.
JOIN + GROUP BY #
Eng kuchli kombinatsiya - hisobotlar shundan iborat:
-- Har bir guruhdagi talabalar soni va o'rtacha bahosi
SELECT
g.nomi AS guruh,
COUNT(t.id) AS talabalar,
ROUND(AVG(t.ortacha_baho), 2) AS ortacha,
MAX(t.ortacha_baho) AS eng_yuqori
FROM guruhlar g
LEFT JOIN talabalar t ON t.guruh_id = g.id
GROUP BY g.id, g.nomi
ORDER BY ortacha DESC;
+---------+-----------+---------+------------+
| guruh | talabalar | ortacha | eng_yuqori |
+---------+-----------+---------+------------+
| IT-102 | 2 | 4.05 | 4.90 |
| IT-101 | 3 | 4.17 | 4.60 |
| IT-201 | 2 | 3.90 | 4.30 |
| DIZ-101 | 0 | NULL | NULL |
+---------+-----------+---------+------------+
-- Eng ko'p sotilgan mahsulotlar
SELECT
mh.nomi,
SUM(be.soni) AS sotilgan,
SUM(be.soni * be.narx) AS tushum,
COUNT(DISTINCT b.mijoz_id) AS mijozlar
FROM mahsulotlar mh
JOIN buyurtma_elementlari be ON be.mahsulot_id = mh.id
JOIN buyurtmalar b ON b.id = be.buyurtma_id
WHERE b.holat IN ('tolangan', 'yetkazildi')
GROUP BY mh.id, mh.nomi
ORDER BY tushum DESC
LIMIT 10;
COUNT(*) va LEFT JOIN-- XATO: talabasi yo'q guruhda ham 1 chiqadi
SELECT g.nomi, COUNT(*) FROM guruhlar g
LEFT JOIN talabalar t ON t.guruh_id = g.id
GROUP BY g.id;
-- TO'G'RI: NULL sanalmaydi
SELECT g.nomi, COUNT(t.id) FROM guruhlar g
LEFT JOIN talabalar t ON t.guruh_id = g.id
GROUP BY g.id;
COUNT(*) qatorlarni sanaydi (NULL qator ham bitta qator).
COUNT(t.id) esa NULL bo'lmagan qiymatlarni sanaydi.
O'ziga JOIN (self join) #
Bir jadvalni o'zi bilan birlashtirish:
CREATE TABLE xodimlar (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
ism VARCHAR(120) NOT NULL,
lavozim VARCHAR(80),
rahbar_id INT UNSIGNED NULL,
FOREIGN KEY (rahbar_id) REFERENCES xodimlar (id) ON DELETE SET NULL
) ENGINE=InnoDB;
INSERT INTO xodimlar (ism, lavozim, rahbar_id) VALUES
('Aziz Karimov', 'Direktor', NULL),
('Dilnoza Yusupova','Bo''lim boshlig''i', 1),
('Sardor Aliyev', 'Dasturchi', 2),
('Malika Tosheva', 'Dasturchi', 2);
-- Har bir xodim va uning rahbari
SELECT
x.ism AS xodim,
x.lavozim,
r.ism AS rahbar
FROM xodimlar x
LEFT JOIN xodimlar r ON r.id = x.rahbar_id;
+-------------------+-------------------+-------------------+
| xodim | lavozim | rahbar |
+-------------------+-------------------+-------------------+
| Aziz Karimov | Direktor | NULL |
| Dilnoza Yusupova | Bo'lim boshlig'i | Aziz Karimov |
| Sardor Aliyev | Dasturchi | Dilnoza Yusupova |
| Malika Tosheva | Dasturchi | Dilnoza Yusupova |
+-------------------+-------------------+-------------------+
CROSS JOIN #
Barcha kombinatsiyalar:
-- Har bir o'lcham va rang kombinatsiyasi
SELECT o.nomi AS olcham, r.nomi AS rang
FROM olchamlar o
CROSS JOIN ranglar r;
CROSS JOIN ni tasodifan yozib qo'yish-- ON shartisiz JOIN - bu CROSS JOIN
SELECT * FROM talabalar, guruhlar;
1000 ta talaba × 50 ta guruh = 50 000 qator. Katta jadvallarda bu serverni bloklaydi.
Har doim JOIN ... ON ... shaklida yozing. Eski FROM a, b WHERE sintaksisidan qoching.
UNION - natijalarni birlashtirish #
JOIN ustunlarni, UNION esa qatorlarni birlashtiradi:
SELECT ism, 'talaba' AS turi FROM talabalar
UNION
SELECT ism, 'o''qituvchi' FROM oqituvchilar
ORDER BY ism;
-- Takrorlarni ham qoldirish (tezroq)
SELECT shahar FROM talabalar
UNION ALL
SELECT shahar FROM oqituvchilar;
UNION | UNION ALL | |
|---|---|---|
| Takrorlar | O'chiriladi | Qoladi |
| Tezlik | Sekinroq | Tezroq |
UNION ALL ishlating - u saralash va takrorlarni tekshirish bosqichini o'tkazib yuboradi.
Amaliy misollar #
-- 1. Har bir talabaning fanlari va baholari
SELECT
CONCAT(t.ism, ' ', t.familiya) AS talaba,
f.nomi AS fan,
tf.baho
FROM talabalar t
JOIN talaba_fan tf ON tf.talaba_id = t.id
JOIN fanlar f ON f.id = tf.fan_id
ORDER BY talaba, fan;
-- 2. Hech qanday fan olmagan talabalar
SELECT t.ism, t.familiya
FROM talabalar t
LEFT JOIN talaba_fan tf ON tf.talaba_id = t.id
WHERE tf.talaba_id IS NULL;
-- 3. Har bir fan bo'yicha statistika
SELECT
f.nomi AS fan,
COUNT(tf.talaba_id) AS talabalar,
ROUND(AVG(tf.baho), 2) AS ortacha_baho,
SUM(tf.baho >= 4) AS yaxshi_baholar
FROM fanlar f
LEFT JOIN talaba_fan tf ON tf.fan_id = f.id
GROUP BY f.id, f.nomi
ORDER BY ortacha_baho DESC;
-- 4. Mijozlar va ularning buyurtmalari summasi
SELECT
m.ism,
m.email,
COUNT(DISTINCT b.id) AS buyurtmalar,
IFNULL(SUM(be.soni * be.narx), 0) AS jami_xarid,
MAX(b.yaratilgan) AS oxirgi_buyurtma
FROM mijozlar m
LEFT JOIN buyurtmalar b ON b.mijoz_id = m.id AND b.holat <> 'bekor'
LEFT JOIN buyurtma_elementlari be ON be.buyurtma_id = b.id
GROUP BY m.id, m.ism, m.email
ORDER BY jami_xarid DESC;
JOIN va tezlik #
JOIN ustunlariga indeks qo'ying-- Tashqi kalit ustuniga indeks MAJBURIY
CREATE INDEX idx_guruh ON talabalar (guruh_id);
Indekssiz JOIN da baza har bir chap qator uchun butun o'ng jadvalni
skanerlaydi. 1000 × 1000 = million amal.
Indeks bilan esa har bir qidiruv O(log n) bo'ladi.
MySQL da FOREIGN KEY yaratilganda indeks avtomatik qo'shiladi, lekin
oddiy JOIN ustunlarida buni qo'lda qilish kerak.
kutubxona bazasida:
- Barcha kitoblar va ularning mualliflarini
JOINbilan chiqaring. LEFT JOINbilan barcha mualliflarni ko'rsating - kitobi yo'qlari ham.- Hech qanday kitobi bo'lmagan mualliflarni toping (
IS NULL). - Har bir muallifning nechta kitobi borligini
JOIN+GROUP BYbilan sanang. ijaralarjadvalini qo'shib, kim qaysi kitobni olganini ko'rsating (3 jadval).- Hech qachon ijaraga olinmagan kitoblarni toping.
ONvaWHEREda shart qo'yish farqini sinab ko'ring.JOINustunlariga indeks qo'shing vaEXPLAINbilan farqni ko'ring.
Xulosa #
INNER JOINfaqat mos kelgan qatorlarni,LEFT JOINchap jadvaldagi hammasini qaytaradi.LEFT JOIN ... WHERE o'ng.id IS NULL- "bog'liq yozuvi yo'qlarni topish" naqshi.LEFT JOINda o'ng jadval shartiONda bo'lishi kerak,WHEREda emas.COUNT(*)o'rnigaCOUNT(o'ng_jadval.id)ishlating.- O'ziga
JOINierarxiya (rahbar-xodim, kategoriya-ichki kategoriya) uchun. ONshartisizJOIN- buCROSS JOIN, ehtiyot bo'ling.JOINustunlariga indeks majburiy - aks holda so'rov juda sekin ishlaydi.UNIONqatorlarni birlashtiradi; takror kerak bo'lmasaUNION ALLtezroq.
Keyingi bo'limda ichki so'rovlarni o'rganamiz.
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.