Correlated Subquery — EXISTS, NOT EXISTS
Muammodan boshlaylik
O'tgan darsdagi subquery lar mustaqil edi: SELECT AVG(narx) FROM mahsulotlar ni alohida ishga tushirsa ham ishlaydi.
Endi savol: "O'z toifasidagi o'rtacha narxdan qimmat mahsulotlar". Telefon uchun — telefonlar o'rtachasi, noutbuk uchun — noutbuklar o'rtachasi. Har bir mahsulot uchun boshqa o'rtacha kerak.
Subquery tashqi so'rovning joriy qatoriga bog'liq bo'lishi kerak. Bu — correlated subquery.
Correlated subquery
SELECT p.nom, p.toifa_id, p.narx
FROM mahsulotlar p
WHERE p.narx > (
SELECT AVG(p2.narx)
FROM mahsulotlar p2
WHERE p2.toifa_id = p.toifa_id -- ← tashqi so'rovdagi p ga ishora
);
+----------------+----------+----------+
| nom | toifa_id | narx |
+----------------+----------+----------+
| iPhone 15 | 1 | 12500000 |
| MacBook Air M3 | 2 | 16900000 |
| AirPods Pro | 3 | 3100000 |
+----------------+----------+----------+
Qanday ishlaydi (mantiqan):
p = iPhone 15 (toifa 1) → ichki: AVG toifa 1 = 8 650 000 → 12 500 000 > 8 650 000 ✅
p = Samsung (toifa 1) → ichki: AVG toifa 1 = 8 650 000 → 4 800 000 > ... ❌
p = MacBook (toifa 2) → ichki: AVG toifa 2 = 12 050 000 → 16 900 000 > ... ✅
...
Ichki so'rov har bir tashqi qator uchun qayta baholanadi. Uni alohida ishga tushirib bo'lmaydi — p.toifa_id nima ekani noma'lum.
Taxalluslar majburiy: p (tashqi) va p2 (ichki) — aks holda MySQL qaysi toifa_id nazarda tutilganini bilmaydi.
EXISTS — "kamida bittasi bormi?"
EXISTS (subquery) — subquery kamida bitta qator qaytarsa TRUE. Qaytgan qiymat muhim emas — faqat qator borligi.
-- Kamida bitta buyurtma bergan mijozlar
SELECT m.ism
FROM mijozlar m
WHERE EXISTS (
SELECT 1 FROM buyurtmalar b WHERE b.mijoz_id = m.id
);
SELECT 1 — odat. SELECT * yoki SELECT b.id ham bir xil ishlaydi: EXISTS ustunlarga qaramaydi.
Tezlik afzalligi: MySQL birinchi mos qatorni topishi bilan to'xtaydi. Mijozning 10 000 ta buyurtmasi bo'lsa ham — bittasini topdi, yetarli. COUNT(*) > 0 esa hammasini sanab chiqadi.
-- ❌ Keraksiz ish
WHERE (SELECT COUNT(*) FROM buyurtmalar b WHERE b.mijoz_id = m.id) > 0
-- ✅
WHERE EXISTS (SELECT 1 FROM buyurtmalar b WHERE b.mijoz_id = m.id)
NOT EXISTS — "hech biri yo'q"
-- Hech qachon buyurtma bermagan mijozlar
SELECT m.ism, m.email
FROM mijozlar m
WHERE NOT EXISTS (
SELECT 1 FROM buyurtmalar b WHERE b.mijoz_id = m.id
);
-- Nodira Usmonova
-- Hech qachon sotilmagan mahsulotlar
SELECT p.nom FROM mahsulotlar p
WHERE NOT EXISTS (
SELECT 1 FROM buyurtma_qatorlari q WHERE q.mahsulot_id = p.id
);
-- Lenovo IdeaPad
NOT EXISTS va NOT IN
O'tgan darsda NOT IN ning NULL tuzog'ini ko'rdik. NOT EXISTS da bu muammo yo'q — u faqat "qator bormi?" deb so'raydi, qiymatlarni solishtirmaydi.
NOT IN |
NOT EXISTS |
LEFT JOIN ... IS NULL |
|
|---|---|---|---|
NULL bilan xavfsiz |
❌ | ✅ | ✅ |
| Murakkab shart | Qiyin | Oson | O'rtacha |
| O'qilishi | Oson | O'rtacha | O'rtacha |
Qoida: "yo'q" savollari uchun — NOT EXISTS yoki LEFT JOIN ... IS NULL. NOT IN (subquery) dan qoching.
Murakkab shartlar bilan EXISTS
EXISTS ichida istalgan shart yozish mumkin — bu uning asosiy kuchi:
-- Oxirgi 30 kunda yetkazilgan buyurtmasi bor mijozlar
SELECT m.ism FROM mijozlar m
WHERE EXISTS (
SELECT 1 FROM buyurtmalar b
WHERE b.mijoz_id = m.id
AND b.holat = 'yetkazildi'
AND b.yaratilgan >= NOW() - INTERVAL 30 DAY
);
-- AirPods sotib olgan, lekin hech qachon qopqoq olmagan mijozlar (cross-sell uchun)
SELECT m.ism FROM mijozlar m
WHERE EXISTS (
SELECT 1 FROM buyurtmalar b
JOIN buyurtma_qatorlari q ON q.buyurtma_id = b.id
WHERE b.mijoz_id = m.id AND q.mahsulot_id = 5
)
AND NOT EXISTS (
SELECT 1 FROM buyurtmalar b
JOIN buyurtma_qatorlari q ON q.buyurtma_id = b.id
WHERE b.mijoz_id = m.id AND q.mahsulot_id = 6
);
Har bir EXISTS — alohida, mustaqil savol. Ularni AND/OR bilan erkin birlashtirasiz. JOIN bilan bunday so'rov ancha chalkash bo'lardi.
Correlated subquery SELECT ichida
Har bir qator uchun hisoblangan qiymat:
SELECT
m.ism,
(SELECT MAX(b.yaratilgan) FROM buyurtmalar b WHERE b.mijoz_id = m.id) AS oxirgi_buyurtma,
(SELECT COUNT(*) FROM buyurtmalar b WHERE b.mijoz_id = m.id) AS jami
FROM mijozlar m;
+-----------------+---------------------+------+
| ism | oxirgi_buyurtma | jami |
+-----------------+---------------------+------+
| Aziz Karimov | 2026-10-12 18:40:00 | 2 |
| Malika Rahimova | 2026-11-01 16:20:00 | 2 |
| Jasur Toshev | 2026-10-20 09:30:00 | 1 |
| Nodira Usmonova | NULL | 0 |
+-----------------+---------------------+------+
Tezlik haqida
"Har bir qator uchun qayta bajariladi" — sekin eshitiladi. Amalda:
- MySQL 8 optimizatori ko'p correlated subquery larni ichki ravishda JOIN yoki semi-join ga aylantiradi
EXISTSbirinchi topilgan qatorda to'xtaydi- Ichki so'rovdagi ustunda (
b.mijoz_id) indeks bo'lsa — har bir tekshiruv bir zumda
Indekssiz esa — haqiqatan sekin: 10 000 mijoz × 1 000 000 buyurtma bo'ylab to'liq ko'rib chiqish. Shuning uchun correlated subquery dagi bog'lash ustuni doim indekslangan bo'lsin (foreign key lar avtomatik indekslanadi).
Nima uchun shunday?
Nima uchun EXISTS ichida SELECT 1 yozish odat bo'lgan?
Tarixan ba'zi bazalar EXISTS (SELECT * ...) da haqiqatan ham barcha ustunlarni o'qib chiqardi. SELECT 1 — optimizatorga va kodni o'qiyotgan odamga "menga qiymatlar kerak emas, faqat qator borligi" degan aniq signal. Bugungi MySQL da farq yo'q — lekin odat qoldi, chunki u niyatni aniq ifodalaydi.
Xulosa
Eslab qoling:
- Correlated subquery — tashqi qatorga ishora qiladi (
WHERE p2.toifa_id = p.toifa_id) - Har bir tashqi qator uchun qayta baholanadi; taxalluslar majburiy
EXISTS— "kamida bittasi bormi?"; birinchi topilganda to'xtaydiCOUNT(*) > 0o'rniga —EXISTSNOT EXISTS— "hech biri yo'q";NULLbilan xavfsiz,NOT INdan yaxshiroq- Bir nechta
EXISTS/NOT EXISTSniANDbilan birlashtirish — murakkab segmentlar - Bog'lash ustuni indekslangan bo'lsin
Keyingi dars
23-dars: CTE — WITH, rekursiv CTE