Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. Scalar ichki so'rov
- 2.1 Bitta qiymat qaytaradigan so'rov
- 2.2 Bir nechta qator — xato, nol qator — NULL
- 3. IN va EXISTS
- 3.1 Ro'yxatda bormi?
- 3.2 Qaysi biri tezroq?
- 3.3 NOT IN va NULL tuzog'i
- 4. Correlated ichki so'rov
- 4.1 Har qatorning o'z o'rtachasi
- 4.2 Anti-join: tegi yo'q vazifalar
- 5. FROM ichida ichki so'rov
- 5.1 Hosila jadval
- 5.2 Hosila jadval bilan JOIN
- 6. LATERAL
- 6.1 "Har foydalanuvchining oxirgi 2 vazifasi"
- 6.2 LEFT JOIN LATERAL ... ON true
- 6.3 LATERAL'siz — xato
- 7. Ko'p uchraydigan xatolar
- 7.1 more than one row returned by a subquery used as an expression
- 7.2 NOT IN bo'sh natija berdi
- 7.3 invalid reference to FROM-clause entry for table "f"
- 7.4 subquery has too many columns
- 7.5 Correlated ichki so'rov sekin
- 8. Mashqlar
- 1-mashq (oson): hosila jadval
- 2-mashq (o'rta): kichik vazifasi yo'q vazifalar
- 3-mashq (qiyin): eng yaqin muddat
- 4-mashq: SQL qadami — teg bo'yicha filtr
- 9. Real ishda
- Xulosa
- Manbalar
SQL subquery va LATERAL: IN, EXISTS, correlated va "har guruhdan oxirgi N ta"
Qisqacha: Subquery (ichki so'rov) — qavs ichidagi boshqa
SELECT: u bitta qiymat (scalar), qiymatlar ro'yxati (IN), "bormi?" javobi (EXISTS) yoki butun jadval (FROM (...)) beradi. Correlated ichki so'rov tashqi qatorga tayanadi va go'yo har qator uchun qayta bajariladi.LATERALesaFROMdagi ichki so'rovga chapdagi jadvalni ko'rsatadi. "Har foydalanuvchining oxirgi 2 vazifasi" kabi masala shu bilan bitta so'rovda yechiladi.
Bu darsda
- Scalar ichki so'rov yozasiz va u bir nechta qator qaytarganda nima bo'lishini bilasiz.
INvaEXISTSni ishlatasiz, farqini vaNOT INtuzog'ini tushuntirasiz.- Correlated ichki so'rov va anti-join (jufti yo'qlarni topish) yozasiz.
FROMichida so'rov vaLATERALbilan "har guruhdan N ta" masalasini yechasiz.- 25-qism API'si uchun teg bo'yicha filtr so'rovini yozasiz.
Oldin bilishingiz kerak: JOIN'lar, NULL mantiqi, Agregatlar, GROUP BY va HAVING.
1. Nega bu kerak?
JOIN'lar darsida jadvallarni birlashtirdik. Lekin ba'zi savollarga JOIN bilan javob berish noqulay yoki imkonsiz:
- "Har foydalanuvchining oxirgi 2 vazifasi" — JOIN hammasini olib keladi,
LIMIT 2esa butun natijani kesadi, har foydalanuvchini emas. - "O'zining o'rtacha bajarish vaqtidan sekin bajarilgan vazifalar" — avval har odam uchun o'rtachani hisoblash, keyin u bilan solishtirish kerak.
- "
#oilategi bor foydalanuvchilar" — teg tafsiloti kerak emas, faqat "bormi?" javobi.
Bularning hammasi — so'rov ichida yana bir so'rov. O'xshatish: do'konda kassir "bu non qancha?" deb narxlar ro'yxatiga qaraydi, keyin hisobni davom ettiradi. Narxga qarash — kichik ichki savol, asosiy ish — hisob. Ichki so'rov ham shunday: asosiy so'rov o'z ishini qilayotib, kichik savolga javob oladi.
2. Scalar ichki so'rov
2.1 Bitta qiymat qaytaradigan so'rov
Scalar subquery (skalyar ichki so'rov) — aynan bitta qator, bitta ustun qaytaradi va oddiy qiymat o'rnida ishlatiladi. Har foydalanuvchining vazifalari soni, SELECT ro'yxati ichida:
SELECT ism,
(SELECT count(*) FROM vazifalar v
WHERE v.egasi_id = f.id) AS vazifalar
FROM foydalanuvchilar f ORDER BY id; ism | vazifalar
--------------------+-----------
Aziz Karimov | 12
Dilnoza Rahimova | 6
Sardor Tursunov | 6
Madina Yusupova | 9
Javohir Olimov | 6
Gulnora Saidova | 4
Bekzod Nazarov | 4
Shahnoza Ergasheva | 0
(8 rows)Bu kanon tajribasi (PostgreSQL 18.6, vazifalar holat 23). Natija JOIN'lar darsidagi LEFT JOIN + count(v.id) bilan bir xil. Ichki so'rov f.id ni — tashqi so'rovning qatorini — ishlatyapti. Bunday ichki so'rov correlated (bog'liq) deyiladi, u haqda alohida bo'limda gaplashamiz.
Ichki so'rov WHERE da ham ishlaydi. Oxirgi vazifadan 7 kun ichida yaratilganlar (biz ishga tushirdik, PostgreSQL 17.6):
SELECT count(*) AS soni FROM vazifalar
WHERE yaratilgan_vaqt > (
SELECT max(yaratilgan_vaqt) - interval '7 days' FROM vazifalar
); soni
------
11
(1 row)Bu ichki so'rov tashqiga bog'liq emas — u bir marta hisoblanadi va natijasi oddiy son kabi ishlatiladi.
2.2 Bir nechta qator — xato, nol qator — NULL
Scalar ichki so'rov ikki qator qaytarsa:
SELECT (SELECT id FROM vazifalar WHERE egasi_id = 1);ERROR: more than one row returned by a subquery used as an expressionTarjimasi: "ifoda sifatida ishlatilgan ichki so'rov bir nechta qator qaytardi". Postgres 12 ta id dan qaysi birini olishni bilmaydi. Xato bajarish vaqtida chiqadi: bugun bitta qator bo'lib ishlagan so'rov ertaga ikkinchi qator qo'shilganda yiqiladi. Shuning uchun scalar ichki so'rovda bitta qatorni kafolatlang: count, max kabi agregat, LIMIT 1 yoki noyob kalit bo'yicha shart.
Nol qator esa xato emas — natija NULL:
SELECT ism,
(SELECT matn FROM vazifalar v
WHERE v.egasi_id = f.id AND v.ustuvorlik = 'past') AS past
FROM foydalanuvchilar f WHERE f.id IN (3, 7, 8) ORDER BY f.id; ism | past
--------------------+------
Sardor Tursunov |
Bekzod Nazarov |
Shahnoza Ergasheva |
(3 rows)Uchalasida past ustuvorlikdagi vazifa yo'q — bo'sh katak NULL (NULL mantiqi).
Tekshirib ko'ring:
(SELECT matn FROM vazifalar WHERE egasi_id = 1 ORDER BY yaratilgan_vaqt DESC LIMIT 1)scalar ichki so'rov sifatida xavfsizmi?
Javob
Ha. LIMIT 1 ko'pi bilan bitta qatorni kafolatlaydi — "more than one row" xatosi chiqmaydi. Vazifa bo'lmasa NULL keladi. ORDER BY muhim: usiz "qaysi biri bitta" — tasodifiy.
3. IN va EXISTS
3.1 Ro'yxatda bormi?
#oila tegi bor foydalanuvchilar — ikki usulda:
SELECT ism FROM foydalanuvchilar
WHERE id IN (SELECT t.egasi_id FROM teglar t WHERE t.nom = '#oila')
ORDER BY ism; ism
------------------
Aziz Karimov
Dilnoza Rahimova
Madina Yusupova
(3 rows)SELECT ism FROM foydalanuvchilar f
WHERE EXISTS (
SELECT 1 FROM teglar t WHERE t.egasi_id = f.id AND t.nom = '#oila'
)
ORDER BY ism; ism
------------------
Aziz Karimov
Dilnoza Rahimova
Madina Yusupova
(3 rows)| Usul | Qanday o'qiladi |
|---|---|
id IN (SELECT ...) |
id shu ro'yxatda bormi? |
EXISTS (SELECT ...) |
shunday qator kamida bitta bormi? |
EXISTS faqat "bor yoki yo'q" ni tekshiradi — ichkaridagi ustunlar ahamiyatsiz. Shuning uchun SELECT 1 yoziladi. Birinchi mos qator topilishi bilan qidiruv to'xtaydi.
JOIN'dan farqi: #oila bir odamda ikki vazifada bo'lsa ham, IN/EXISTS odamni bir marta qaytaradi. JOIN esa ikki marta qaytarardi (JOIN'lar darsidagi fan-out).
3.2 Qaysi biri tezroq?
Ko'p qo'llanmalarda "EXISTS IN dan tez" deyiladi. Postgres'da bu odatda to'g'ri emas: planner (Postgres'ning so'rovni qanday bajarishni hal qiladigan qismi) ikkalasini ham bir xil rejaga (plan) aylantiradi. Biz tekshirdik (PostgreSQL 17.6, EXPLAIN — so'rov rejasini ko'rsatadi). Chiqishda faqat birinchi qatorga qarang — bajarish usuli Hash Join:
EXPLAIN (COSTS OFF)
SELECT ism FROM foydalanuvchilar
WHERE id IN (SELECT t.egasi_id FROM teglar t WHERE t.nom = '#oila'); QUERY PLAN
-------------------------------------------------
Hash Join
Hash Cond: (foydalanuvchilar.id = t.egasi_id)
-> Seq Scan on foydalanuvchilar
-> Hash
-> Seq Scan on teglar t
Filter: (nom = '#oila'::text)
(6 rows)EXISTS varianti uchun ham aynan shu Hash Join rejasi chiqdi. Rejani o'qishni EXPLAIN ANALYZE'ni o'qish darsida o'rganamiz. Hozirgi xulosa: tanlovni tezlik emas, ma'no va NULL xulq-atvori belgilasin — keyingi bo'lim.
3.3 NOT IN va NULL tuzog'i
NULL mantiqi darsida ko'rgan edingiz: ro'yxatda bitta NULL bo'lsa, NOT IN hech qachon rost bo'lmaydi va 0 qator qaytaradi. Bizda vazifalar.egasi_id NOT NULL, shuning uchun bu ishlaydi:
SELECT ism FROM foydalanuvchilar
WHERE id NOT IN (SELECT egasi_id FROM vazifalar); ism
--------------------
Shahnoza Ergasheva
(1 row)Lekin ichki so'rov NULL bo'lishi mumkin ustunni (masalan ota_id) qaytarsa, natija jimgina bo'sh bo'ladi. NOT EXISTS da bunday tuzoq yo'q. Qoida: "yo'q"ni tekshirishda NOT EXISTS ishlating.
Tekshirib ko'ring:
WHERE id NOT IN (SELECT ota_id FROM vazifalar)— "hech kimning otasi bo'lmagan vazifalar" ni topadimi?
Javob
Yo'q — 0 qator qaytaradi. Ko'p vazifada ota_id NULL, ro'yxatga NULL tushadi va id NOT IN (..., NULL) har qatorda NULL (rost emas) bo'ladi. To'g'risi: WHERE NOT EXISTS (SELECT 1 FROM vazifalar k WHERE k.ota_id = v.id) yoki ichki so'rovga WHERE ota_id IS NOT NULL qo'shish.
4. Correlated ichki so'rov
4.1 Har qatorning o'z o'rtachasi
Vazifa: har foydalanuvchida o'zining o'rtacha bajarish vaqtidan sekin bajarilgan vazifalar. "O'rtacha" har odam uchun boshqa — ichki so'rov tashqi qatorning egasini bilishi kerak:
SELECT v.egasi_id, v.matn,
v.bajarilgan_vaqt - v.yaratilgan_vaqt AS vaqt
FROM vazifalar v
WHERE v.bajarildi
AND v.bajarilgan_vaqt - v.yaratilgan_vaqt > (
SELECT avg(x.bajarilgan_vaqt - x.yaratilgan_vaqt)
FROM vazifalar x
WHERE x.egasi_id = v.egasi_id AND x.bajarildi
)
ORDER BY 1, 3 DESC; egasi_id | matn | vaqt
----------+-----------------------------------------------+-----------------
1 | Blog uchun mavzu tanlash | 22:55:00
2 | Gaz to'lovi #to'lov | 2 days
2 | Ingliz tili: 20 ta yangi so'z #o'qish #ingliz | 1 day 02:00:00
3 | Moy almashtirish #mashina | 4 days 01:00:00
4 | Adabiyotlar ro'yxati | 5 days 02:54:00
4 | Yotoqxona to'lovi #to'lov | 3 days 04:00:00
5 | Mijozga taklifnoma yuborish #ish | 02:00:00
6 | Un va yog' xarid qilish #bozor #oshxona | 05:00:00
7 | Algoritmlar: 5 ta masala #o'qish | 2 days 03:00:00
(9 rows)Bir jadval ikki rolda: v — tekshirilayotgan vazifa, x — o'rtacha uchun "hamkasblar". x.egasi_id = v.egasi_id — bog'liqlik nuqtasi. Vaqt ayirmasi interval turida (Ifodalar va funksiyalar), avg intervallarning o'rtachasini hisoblay oladi.
flowchart LR
T["tashqi qator v<br/>egasi_id = 2"] --> I["ichki so'rov:<br/>2 ning o'rtachasi"]
I --> C{"v ning vaqti<br/>kattami?"}
C -->|ha| R["natijaga"]
C -->|yo'q| S["tashlanadi"]Diagrammadagi aylana har tashqi qator uchun takrorlanadi — mantiqan ichki so'rov qator soni marta bajariladi. Postgres ba'zan buni optimallashtiradi, ba'zan yo'q. 47 qatorda farq sezilmaydi, millionlab qatorda esa correlated ichki so'rov sekinlashishi mumkin. Unda o'rtachalarni bir marta hisoblab, JOIN qilish yaxshiroq — keyingi bo'limda.
4.2 Anti-join: tegi yo'q vazifalar
Anti-join — "jufti yo'qlarni top". NOT EXISTS bilan:
SELECT v.id, v.matn FROM vazifalar v
WHERE NOT EXISTS (
SELECT 1 FROM vazifa_teglari vt WHERE vt.vazifa_id = v.id
)
ORDER BY v.id; id | matn
----+-----------------------------
38 | Velosiped zanjirini moylash
41 | Jadvallarni loyihalash
42 | Adabiyotlar ro'yxati
43 | Blog uchun mavzu tanlash
44 | Qoralama yozish
45 | Rasmlarni siqish
46 | ER diagramma chizish
47 | Normalizatsiyani tekshirish
(8 rows)8 ta — JOIN'lar darsidagi FULL JOIN hisobidagi "tegsiz vazifa" soni bilan bir xil. Kichik vazifalar matnida # yo'q, shuning uchun ularning hammasi shu ro'yxatda. Uchta yo'l bilan bir xil natija olish mumkin:
| Yo'l | Izoh |
|---|---|
NOT EXISTS (...) |
aniq ma'no, NULL tuzog'i yo'q — tavsiya |
LEFT JOIN ... WHERE o'ng.id IS NULL |
JOIN uslubi, natija bir xil |
NOT IN (...) |
ichida NULL bo'lsa — 0 qator |
5. FROM ichida ichki so'rov
5.1 Hosila jadval
Ichki so'rov FROM da butun jadval vazifasini bajaradi — hosila jadval (derived table). Masalan "bir odamga o'rtacha nechta vazifa to'g'ri keladi?" — avval har odamning sonini, keyin ularning o'rtachasini olamiz (biz ishga tushirdik, PostgreSQL 17.6):
SELECT round(avg(soni), 2) AS ortacha
FROM (SELECT egasi_id, count(*) AS soni
FROM vazifalar GROUP BY egasi_id); ortacha
---------
6.71
(1 row)47 vazifa / 7 kishi = 6.71 (Shahnoza kirmadi — unda vazifa yo'q). Agregatni agregat ustidan to'g'ridan olib bo'lmaydi, shuning uchun ikki bosqich kerak.
PostgreSQL 16 dan oldin FROM dagi ichki so'rovga taxallus majburiy edi: FROM (...) AS s. 16-versiyadan beri ixtiyoriy. Eski versiyalar va boshqa bazalar uchun taxallus yozib qo'yish yaxshi odat.
5.2 Hosila jadval bilan JOIN
«Correlated ichki so'rov» bo'limidagi masalani boshqacha yozamiz: avval har odamning o'rtachasi — bir marta, keyin JOIN:
SELECT v.egasi_id, v.matn
FROM vazifalar v
JOIN (SELECT egasi_id,
avg(bajarilgan_vaqt - yaratilgan_vaqt) AS ortacha
FROM vazifalar WHERE bajarildi
GROUP BY egasi_id) o ON o.egasi_id = v.egasi_id
WHERE v.bajarildi
AND v.bajarilgan_vaqt - v.yaratilgan_vaqt > o.ortacha
ORDER BY 1, 2; egasi_id | matn
----------+-----------------------------------------------
1 | Blog uchun mavzu tanlash
2 | Gaz to'lovi #to'lov
2 | Ingliz tili: 20 ta yangi so'z #o'qish #ingliz
3 | Moy almashtirish #mashina
4 | Adabiyotlar ro'yxati
4 | Yotoqxona to'lovi #to'lov
5 | Mijozga taklifnoma yuborish #ish
6 | Un va yog' xarid qilish #bozor #oshxona
7 | Algoritmlar: 5 ta masala #o'qish
(9 rows)O'sha 9 ta vazifa (biz ishga tushirdik, PostgreSQL 17.6). Endi o'rtachalar 7 marta emas, bitta guruhlash bilan hisoblanadi. Ichma-ich qavslar ko'paysa o'qish qiyinlashadi — keyingi darsda CTE bu bosqichlarga nom beradi.
Hosila jadvalsiz avg(count(*)) yozsak — ERROR: aggregate function calls cannot be nested ("agregat funksiya chaqiruvlari ichma-ich bo'lolmaydi").
6. LATERAL
6.1 "Har foydalanuvchining oxirgi 2 vazifasi"
Oddiy hosila jadval tashqi qatorni ko'rmaydi — u bir marta, mustaqil hisoblanadi. LATERAL kalit so'zi buni o'zgartiradi: FROM dagi ichki so'rov o'zidan chapdagi jadvallarning joriy qatorini ko'radi. Ya'ni u har chap qator uchun alohida bajariladi. Natijada har ism necha marta chiqqaniga qarang:
SELECT f.ism, oxirgi.matn, oxirgi.yaratilgan_vaqt
FROM foydalanuvchilar f
CROSS JOIN LATERAL (
SELECT v.matn, v.yaratilgan_vaqt FROM vazifalar v
WHERE v.egasi_id = f.id
ORDER BY v.yaratilgan_vaqt DESC LIMIT 2
) AS oxirgi
ORDER BY f.id, oxirgi.yaratilgan_vaqt DESC; ism | matn | yaratilgan_vaqt
------------------+-------------------------------------------------+------------------------
Aziz Karimov | Velosiped zanjirini moylash | 2026-10-18 12:20:00+00
Aziz Karimov | SQL JOIN mashqlarini yechish #o'qish | 2026-10-14 14:30:00+00
Dilnoza Rahimova | Rezyume yangilash #ish | 2026-10-16 17:00:00+00
Dilnoza Rahimova | Gaz to'lovi #to'lov | 2026-10-11 04:30:00+00
Sardor Tursunov | Futbol: payshanba 20:00 #sport | 2026-10-15 14:00:00+00
Sardor Tursunov | Soliq hisobotini topshirish #ish #to'lov | 2026-10-13 06:00:00+00
Madina Yusupova | Uyga qo'ng'iroq qilish #oila | 2026-10-17 16:10:00+00
Madina Yusupova | Python laboratoriya ishi #o'qish | 2026-10-12 18:40:00+00
Javohir Olimov | Sport zalga yozilish #sport | 2026-10-19 02:30:00+00
Javohir Olimov | Yangi xodimga kirish huquqi berish #ish #server | 2026-10-09 09:00:00+00
Gulnora Saidova | Instagram uchun surat olish #ish | 2026-10-12 10:30:00+00
Gulnora Saidova | Un va yog' xarid qilish #bozor #oshxona | 2026-10-07 02:15:00+00
Bekzod Nazarov | Do'stlar bilan choyxona #dam | 2026-10-18 14:00:00+00
Bekzod Nazarov | Noutbukka SSD qo'yish #ta'mir | 2026-10-14 08:45:00+00
(14 rows)7 kishi × 2 = 14 qator. LIMIT 2 endi har foydalanuvchi uchun ishlaydi — "Nega bu kerak?" bo'limidagi birinchi savolga javob. Tashqi ORDER BY natijani chiroyli tartiblaydi, ichki ORDER BY esa "qaysi 2 ta" ni tanlaydi.
O'xshatish: navbatdagi har mijoz uchun sotuvchi omborga alohida borib, aynan shu mijozga kerak ikki narsani olib chiqadi. LATERAL — "har chap qator uchun alohida bor".
6.2 LEFT JOIN LATERAL ... ON true
Shahnoza yuqorida yo'q — CROSS JOIN jufti yo'qni tashlaydi. Uni saqlash uchun LEFT JOIN LATERAL. Lekin LEFT JOIN ON talab qiladi, shart esa allaqachon ichki so'rovda — shuning uchun ON true ("har doim juft"):
SELECT f.ism, o.matn FROM foydalanuvchilar f
LEFT JOIN LATERAL (
SELECT matn FROM vazifalar v WHERE v.egasi_id = f.id
ORDER BY yaratilgan_vaqt DESC LIMIT 1
) o ON true
ORDER BY f.id; ism | matn
--------------------+----------------------------------
Aziz Karimov | Velosiped zanjirini moylash
Dilnoza Rahimova | Rezyume yangilash #ish
Sardor Tursunov | Futbol: payshanba 20:00 #sport
Madina Yusupova | Uyga qo'ng'iroq qilish #oila
Javohir Olimov | Sport zalga yozilish #sport
Gulnora Saidova | Instagram uchun surat olish #ish
Bekzod Nazarov | Do'stlar bilan choyxona #dam
Shahnoza Ergasheva |
(8 rows)6.3 LATERAL'siz — xato
SELECT f.ism, o.matn FROM foydalanuvchilar f,
(SELECT matn FROM vazifalar v WHERE v.egasi_id = f.id LIMIT 1) o;ERROR: invalid reference to FROM-clause entry for table "f"
LINE 2: ... (SELECT matn FROM vazifalar v WHERE v.egasi_id = f.id LIMIT...
^
DETAIL: There is an entry for table "f", but it cannot be referenced from this part of the query.
HINT: To reference that table, you must mark this subquery with LATERAL.Tarjimasi: "f jadvaliga noto'g'ri havola. Tafsilot: f bor, lekin so'rovning bu qismidan unga murojaat qilib bo'lmaydi. Maslahat: buning uchun ichki so'rovni LATERAL deb belgilang". Postgres xabarining o'zi yechimni aytyapti.
UPSERT va MERGE darsida CROSS JOIN LATERAL teglarni_ajrat(v.matn) ni ko'rgan edingiz. Endi tushunarli: funksiya har vazifa uchun uning matni bilan chaqiriladi. FROM dagi funksiyalar uchun LATERAL so'zi ixtiyoriy — Postgres ularni o'zi lateral deb hisoblaydi. Ichki so'rov uchun esa majburiy.
Tekshirib ko'ring: "har foydalanuvchining oxirgi 2 vazifasi" ni
LATERALsiz, oddiyJOIN ... ORDER BY ... LIMIT 14bilan yozsa bo'ladimi?
Javob
Yo'q. LIMIT 14 butun natijadan eng yangi 14 tani oladi — faol odamning 5 vazifasi kirib, boshqasiniki kirmasligi mumkin. "Har guruhdan N ta" uchun ikki yo'l bor: LATERAL (shu dars) yoki window funksiya row_number() (Window funksiyalar 1).
7. Ko'p uchraydigan xatolar
7.1 more than one row returned by a subquery used as an expression
Scalar ichki so'rov bir nechta qator qaytardi. Agregat, LIMIT 1 yoki noyob shart qo'shing. Ehtimol sizga IN yoki EXISTS kerak edi.
7.2 NOT IN bo'sh natija berdi
Ichki so'rovda NULL bor. NOT EXISTS ga o'ting.
7.3 invalid reference to FROM-clause entry for table "f"
FROM dagi ichki so'rov tashqi jadvalni ishlatyapti — LATERAL qo'shing.
7.4 subquery has too many columns
id IN (SELECT id, matn FROM ...) — IN uchun ichki so'rov bitta ustun qaytarishi kerak (yoki chap tomonda ham shuncha ustun: (a, b) IN (SELECT x, y ...)).
7.5 Correlated ichki so'rov sekin
Katta jadvalda har qator uchun qayta hisoblash qimmat. Hosila jadval + JOIN yoki CTE bilan bir marta hisoblang, ichki so'rovdagi shart ustuniga indeks qo'ying.
8. Mashqlar
Mashq fayllari kurs/mashqlar/26/25-subquery/ da. Hammasi vazifalar holat 23 da.
1-mashq (oson): hosila jadval
Kamida 4 ta bajarilgan vazifasi bor foydalanuvchilarni chiqaring: ism, soni; soni bo'yicha kamayish, keyin ism. HAVING ishlatmang — FROM ichidagi so'rov bilan yozing. Yordam: «FROM ichida ichki so'rov» bo'limi.
Yechim
SELECT f.ism, s.soni
FROM foydalanuvchilar f
JOIN (SELECT egasi_id, count(*) AS soni FROM vazifalar
WHERE bajarildi GROUP BY egasi_id) s ON s.egasi_id = f.id
WHERE s.soni >= 4
ORDER BY s.soni DESC, f.ism; ism | soni
-----------------+------
Aziz Karimov | 5
Madina Yusupova | 4
(2 rows)Hosila jadval s oddiy jadval kabi JOIN qilindi, uning soni ustuni esa tashqi WHERE da ishlatildi. HAVING count(*) >= 4 bilan ham yozish mumkin edi — ikkalasi to'g'ri.
2-mashq (o'rta): kichik vazifasi yo'q vazifalar
Madinaning (egasi_id = 4) yuqori darajadagi (ota_id IS NULL) vazifalaridan kichik vazifasi yo'qlarini chiqaring: id, matn. Yordam: anti-join — NOT EXISTS, ichki so'rovda k.ota_id = v.id.
Yechim
SELECT v.id, v.matn FROM vazifalar v
WHERE v.egasi_id = 4 AND v.ota_id IS NULL
AND NOT EXISTS (
SELECT 1 FROM vazifalar k WHERE k.ota_id = v.id
)
ORDER BY v.id; id | matn
----+-------------------------------------
13 | Kutubxonaga kitob qaytarish #o'qish
18 | Yotoqxona to'lovi #to'lov
31 | Python laboratoriya ishi #o'qish
37 | Uyga qo'ng'iroq qilish #oila
(4 rows)7-vazifa ("Kurs ishi") chiqmadi — uning kichik vazifalari bor. NOT IN (SELECT ota_id ...) bilan yozsangiz — 0 qator (ota_id da NULLlar bor).
3-mashq (qiyin): eng yaqin muddat
Har foydalanuvchi uchun eng yaqin muddatli bajarilmagan vazifani chiqaring: ism, matn, muddat. Muddatsiz vazifalar hisobga olinmasin. Hamma foydalanuvchi chiqsin (vazifasi yo'q bo'lsa — bo'sh). Yordam: LEFT JOIN LATERAL (... ORDER BY v.muddat LIMIT 1) ... ON true.
Yechim
SELECT f.ism, oxirgi.matn, oxirgi.muddat
FROM foydalanuvchilar f
LEFT JOIN LATERAL (
SELECT v.matn, v.muddat FROM vazifalar v
WHERE v.egasi_id = f.id AND NOT v.bajarildi
AND v.muddat IS NOT NULL
ORDER BY v.muddat LIMIT 1
) AS oxirgi ON true
ORDER BY f.id; ism | matn | muddat
--------------------+------------------------------------------------+------------------------
Aziz Karimov | Oshxonadagi jo'mrakni almashtirish #ta'mir #uy | 2026-10-20 13:00:00+00
Dilnoza Rahimova | IELTS writing task 2 mashqi #ingliz | 2026-10-24 15:00:00+00
Sardor Tursunov | Do'konga yangi narxlarni kiritish #ish | 2026-10-09 04:00:00+00
Madina Yusupova | Python laboratoriya ishi #o'qish | 2026-10-22 03:30:00+00
Javohir Olimov | Server zaxirasini tekshirish #ish #server | 2026-10-03 13:00:00+00
Gulnora Saidova | Instagram uchun surat olish #ish | 2026-10-30 07:00:00+00
Bekzod Nazarov | Universitet kontrakti #to'lov | 2026-11-01 13:00:00+00
Shahnoza Ergasheva | |
(8 rows)Sardor va Javohirning muddati o'tib ketgan (oktyabr boshi). "Muddati o'tgan" belgisi uchun bu so'rov asos bo'ladi. muddat IS NOT NULL siz ORDER BY da NULLlar oxirida keladi, bu holda ham to'g'ri ishlardi; lekin shart niyatni aniq ko'rsatadi.
4-mashq: SQL qadami — teg bo'yicha filtr
25-qism API'sida GET /vazifalar?teg=%23o'qish kabi filtr bo'ladi. O'z bazangizda Azizning (egasi_id = 1) #o'qish tegli vazifalarini yangidan eskiga chiqaring. Vazifa takrorlanmasin (JOIN emas, EXISTS). Sxema o'zgarmaydi — commit yo'q. Yordam: «IN va EXISTS» bo'limi; ichki so'rovda vazifa_teglari va teglar JOIN qilinadi.
Yechim
SELECT v.id, v.matn
FROM vazifalar v
WHERE v.egasi_id = 1
AND EXISTS (
SELECT 1 FROM vazifa_teglari vt
JOIN teglar t ON t.id = vt.teg_id
WHERE vt.vazifa_id = v.id AND t.nom = '#o''qish'
)
ORDER BY v.yaratilgan_vaqt DESC, v.id DESC; id | matn
----+--------------------------------------------------
34 | SQL JOIN mashqlarini yechish #o'qish
2 | Postgres'ni Docker'da ko'tarish #o'qish #docker
1 | JavaScript kursining 26-qismini boshlash #o'qish
(3 rows)API'da teg nomi foydalanuvchidan keladi — u so'rov matniga qo'shilmaydi, parametr sifatida beriladi (Parametrli so'rovlar va SQL injection). Bir nechta teg bilan filtr (#o'qish VA #docker) — har teg uchun alohida EXISTS.
9. Real ishda
- "Har guruhdan N ta" — oxirgi 3 buyurtma, har kategoriyadan eng arzon 5 mahsulot, har chatning oxirgi xabari (Telegram chat ro'yxati aynan shunday). Postgres'da
LATERAL+ indeks bu masalaning eng tez yechimlaridan biri. EXISTSfiltrlari — "teg bo'yicha", "ruxsati bor", "kamida bitta to'lovi bor". JOIN + DISTINCT o'rniga.- Anti-join — "hali to'lamaganlar", "hech qachon kirmagan foydalanuvchilar", "javobsiz qolgan murojaatlar".
- ORM'larda ichki so'rov ko'pincha "N+1" muammosining yechimi: har qator uchun alohida so'rov o'rniga bitta so'rov (N+1 muammosi).
- Intervyu: "
INvaEXISTSfarqi", "NOT INnega xavfli", "correlated subquery nima" — tez-tez so'raladi.
Xulosa
- Scalar ichki so'rov — bitta qiymat; ikki qator — xato, nol qator —
NULL. IN— ro'yxatda bormi,EXISTS— kamida bitta bormi; Postgres ko'pincha ikkalasini bir xil rejaga aylantiradi.- "Yo'q"ni tekshirishda
NOT EXISTS:NOT INichidaNULLbo'lsa, natija jimgina bo'sh. - Correlated ichki so'rov tashqi qatorga tayanadi — mantiqan har qator uchun qayta bajariladi.
FROM (...)— hosila jadval;LATERALuni chapdagi qatorni ko'radigan qiladi — "har guruhdan N ta" uchun.
Keyingi dars: CTE (WITH) — ichki so'rovlarga nom berib, murakkab so'rovni bosqichlarga bo'lamiz.
Manbalar
- PostgreSQL 18 hujjati — Subquery Expressions (EXISTS, IN, NOT IN): https://www.postgresql.org/docs/18/functions-subquery.html
- PostgreSQL 18 hujjati — LATERAL Subqueries: https://www.postgresql.org/docs/18/queries-table-expressions.html#QUERIES-LATERAL
- PostgreSQL 16 reliz eslatmalari (FROM'dagi ichki so'rov taxallusi ixtiyoriy): https://www.postgresql.org/docs/16/release-16.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!