Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. INNER JOIN
- 2.1 Birinchi birlashtirish
- 2.2 Juftsizlar tushib qoladi
- 3. LEFT JOIN
- 3.1 Chap jadvalning hammasi
- 3.2 count(*) va count(v.id) farqi
- 3.3 RIGHT JOIN
- 4. LEFT JOIN + WHERE tuzog'i
- 4.1 Shahnoza yana yo'qoldi
- 4.2 To'g'risi — shart ON da
- 5. Ko'p jadvalli zanjir
- 5.1 Vazifa → bog'lovchi → teg
- 5.2 Teskari yo'nalish: tegdan odamga
- 5.3 Fan-out: qatorlar ko'payib ketadi
- 6. Self-join: jadval o'zi bilan
- 7. FULL JOIN, USING va CROSS JOIN
- 7.1 RIGHT va FULL — vazifasiz teg
- 7.2 USING
- 7.3 CROSS JOIN
- 8. Ko'p uchraydigan xatolar
- 8.1 column reference "id" is ambiguous
- 8.2 ON siz JOIN
- 8.3 LEFT JOIN Shahnozani "yo'qotdi"
- 8.4 Son kutilganidan katta
- 8.5 count(*) LEFT JOIN'da 0 o'rniga 1 berdi
- 9. Mashqlar
- 1-mashq (oson): kichik vazifalar egasi
- 2-mashq (o'rta): yuqori ustuvorlikdagilar, nol bilan
- 3-mashq (qiyin): teglar soni, fan-out'siz
- 4-mashq: SQL qadami — ro'yxat so'rovi teglar bilan
- 10. Real ishda
- Xulosa
- Manbalar
SQL JOIN: INNER, LEFT, RIGHT, FULL, CROSS va self-join PostgreSQL misollarida
Qisqacha: JOIN (birlashtirish) — ikki jadval qatorlarini shart (
ON) bo'yicha juftlab, bitta natijaga yig'ish.INNER JOINfaqat jufti borlarni qoldiradi,LEFT JOINchap jadvalning hammasini (juftsizda o'ng tomonNULL),FULL JOINikkala tomonning hammasini. Eng ko'p uchraydigan tuzoq:LEFT JOINdan keyin o'ng jadval shartiniWHEREga yozish — uLEFTni jimginaINNERga aylantiradi; shartONga yozilishi kerak.
Bu darsda
INNER,LEFT,RIGHT,FULLvaCROSSJOIN farqini misol bilan tushuntirasiz.- Uch-to'rt jadvalni zanjir qilib birlashtirasiz (vazifa → teg → foydalanuvchi).
- Jadvalni o'zi bilan birlashtirasiz (self-join): kichik vazifa va uning otasi.
LEFT JOIN + WHEREtuzog'ini va qatorlar ko'payib ketishini (fan-out) taniysiz.- 25-qism API'si uchun "vazifalar teglari bilan" so'rovini yozasiz.
Oldin bilishingiz kerak: Foreign key va referential actions, Agregatlar, GROUP BY va HAVING, UPSERT va MERGE.
1. Nega bu kerak?
UPSERT va MERGE darsida teglar va vazifa_teglari jadvallarini qo'shdik. Endi vazifalar bazasida to'rtta jadval bor. Har biri bitta narsani saqlaydi:
erDiagram
foydalanuvchilar ||--o{ vazifalar : "egasi"
foydalanuvchilar ||--o{ teglar : "egasi"
vazifalar |o--o{ vazifalar : "ota_id"
vazifalar ||--o{ vazifa_teglari : "vazifa"
teglar ||--o{ vazifa_teglari : "teg"
foydalanuvchilar {
bigint id PK
text ism
}
vazifalar {
bigint id PK
bigint egasi_id FK
text matn
bigint ota_id FK
}
teglar {
bigint id PK
bigint egasi_id FK
text nom
}
vazifa_teglari {
bigint vazifa_id PK
bigint teg_id PK
}Diagrammadagi har chiziq — tashqi kalit (foreign key). Chiziq uchidagi "panja" belgisi — "ko'p" tomon: bitta foydalanuvchida ko'p vazifa. Bu belgilarni ER modellash darsida batafsil o'qiymiz.
Ma'lumot bo'lingan — bu yaxshi: Azizning ismi bitta joyda, 12 marta takrorlanmaydi. Lekin ekranda bizga birlashgan ko'rinish kerak: "Kranni almashtirish — Aziz Karimov — #ta'mir #uy". Uchta jadvaldan bitta qator. Buni JOIN (birlashtirish) qiladi.
O'xshatish: maktabda ikki ro'yxat bor — o'quvchilar (ism, sinf raqami) va sinflar (raqam, sinf rahbari). "Har o'quvchining sinf rahbari kim?" savoliga javob berish uchun ikkala ro'yxatni sinf raqami bo'yicha yonma-yon qo'yasiz. JOIN aynan shu: umumiy ustun bo'yicha juftlash.
2. INNER JOIN
2.1 Birinchi birlashtirish
Gulnora va Bekzodning vazifalari, egasining ismi bilan:
SELECT v.id, v.matn, f.ism
FROM vazifalar v
JOIN foydalanuvchilar f ON f.id = v.egasi_id
WHERE v.egasi_id IN (6, 7) AND v.ota_id IS NULL
ORDER BY v.id; id | matn | ism
----+---------------------------------------------+-----------------
4 | Somsa uchun xamir tayyorlash #oshxona | Gulnora Saidova
12 | Buyurtmalarni Telegram'da qabul qilish #ish | Gulnora Saidova
19 | Universitet kontrakti #to'lov | Bekzod Nazarov
22 | Un va yog' xarid qilish #bozor #oshxona | Gulnora Saidova
25 | Algoritmlar: 5 ta masala #o'qish | Bekzod Nazarov
30 | Instagram uchun surat olish #ish | Gulnora Saidova
33 | Noutbukka SSD qo'yish #ta'mir | Bekzod Nazarov
39 | Do'stlar bilan choyxona #dam | Bekzod Nazarov
(8 rows)Biz buni o'z bazamizda ishga tushirdik (PostgreSQL 17.6, vazifalar holat 23). Qatorma-qator:
| Qism | Ma'nosi |
|---|---|
FROM vazifalar v |
asosiy jadval, qisqa nomi (taxallus) v |
JOIN foydalanuvchilar f |
ikkinchi jadval, taxallus f |
ON f.id = v.egasi_id |
juftlash sharti: vazifaning egasi = foydalanuvchi id si |
v.matn, f.ism |
qaysi jadvaldan qaysi ustun |
JOIN va INNER JOIN — bir xil narsa, INNER so'zi ixtiyoriy. Postgres har vazifa qatori uchun ON sharti rost bo'lgan foydalanuvchini topadi va ikkalasini bitta qatorga "yopishtiradi".
2.2 Juftsizlar tushib qoladi
Endi har foydalanuvchining vazifalari soni (GROUP BY bilan). Bu va keyingi ko'p misollar — kanon tajribasidan (PostgreSQL 18.6):
SELECT f.ism, count(*) AS vazifalar
FROM foydalanuvchilar f
JOIN vazifalar v ON v.egasi_id = f.id
GROUP BY f.id ORDER BY f.id; ism | vazifalar
------------------+-----------
Aziz Karimov | 12
Dilnoza Rahimova | 6
Sardor Tursunov | 6
Madina Yusupova | 9
Javohir Olimov | 6
Gulnora Saidova | 4
Bekzod Nazarov | 4
(7 rows)Bazada 8 foydalanuvchi bor, natijada 7. Shahnoza Ergasheva yo'q — unda birorta ham vazifa yo'q. INNER JOIN uchun "jufti yo'q qator" mavjud emas. Bu xato emas, INNER'ning ta'rifi: faqat ikkala tomonda jufti borlar.
GROUP BY f.id va SELECT f.ism — id birlamchi kalit (primary key) bo'lgani uchun Postgres ismni ham guruh ichida bir xil deb biladi (Agregatlar darsidagi funksional bog'liqlik).
Tekshirib ko'ring:
JOINdagiON f.id = v.egasi_idniON v.egasi_id = f.iddeb yozsa, natija o'zgaradimi?
Javob
O'zgarmaydi. Tenglik ikki tomonlama: a = b va b = a — bir xil shart. Jadvallar tartibi (FROM vazifalar JOIN foydalanuvchilar yoki teskari) ham INNER JOIN natijasini o'zgartirmaydi — faqat ustunlar tartibi va qaysi jadval "asosiy" ko'rinishi o'zgaradi. LEFT JOIN'da esa tartib muhim — keyingi bo'limda.
3. LEFT JOIN
3.1 Chap jadvalning hammasi
"Hamma foydalanuvchilar va ularning vazifalari soni, nol bo'lsa ham":
SELECT f.ism, count(v.id) AS vazifalar
FROM foydalanuvchilar f
LEFT JOIN vazifalar v ON v.egasi_id = f.id
GROUP BY f.id ORDER BY f.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)LEFT JOIN (chap birlashtirish) — chap (FROM dagi) jadvalning har qatori natijada qoladi. Jufti bo'lmasa, o'ng jadval ustunlari NULL bilan to'ldiriladi. Shahnoza qaytdi: uning qatorida v.id, v.matn — hammasi NULL.
flowchart LR
F["foydalanuvchilar<br/>8 qator"] --> I["INNER JOIN<br/>7 kishi"]
F --> L["LEFT JOIN<br/>8 kishi"]
L --> N["Shahnoza:<br/>v.* = NULL"]Diagrammada ko'ring: bir xil jadval, bir xil shart — INNER'da 7 kishi, LEFT'da 8. Farq — juftsiz qator taqdiri.
3.2 count(*) va count(v.id) farqi
Nega count(v.id), count(*) emas? Tekshiramiz (biz ishga tushirdik, PostgreSQL 17.6):
SELECT f.ism, count(*) AS yulduzcha, count(v.id) AS v_id
FROM foydalanuvchilar f
LEFT JOIN vazifalar v ON v.egasi_id = f.id
WHERE f.id IN (7, 8)
GROUP BY f.id ORDER BY f.id; ism | yulduzcha | v_id
--------------------+-----------+------
Bekzod Nazarov | 4 | 4
Shahnoza Ergasheva | 1 | 0
(2 rows)count(*) qatorlarni sanaydi — Shahnozaning bitta qatori bor (o'ng tomoni NULL bo'lsa ham). count(v.id) esa faqat NULL bo'lmagan qiymatlarni sanaydi (NULL mantiqi). LEFT JOIN'dan keyin sanashda doim o'ng jadvalning NOT NULL ustunini sanang.
3.3 RIGHT JOIN
RIGHT JOIN — LEFT'ning ko'zgudagi aksi: o'ng jadvalning hammasi qoladi. A RIGHT JOIN B = B LEFT JOIN A. Amalda deyarli hamma faqat LEFT yozadi — o'qish oson. RIGHT misolini quyida FULL bilan birga ko'ramiz.
4. LEFT JOIN + WHERE tuzog'i
4.1 Shahnoza yana yo'qoldi
Vazifa: "har foydalanuvchida nechta bajarilmagan vazifa bor, nol bo'lsa ham". Birinchi urinish — shartni WHERE ga:
SELECT f.ism, count(v.id) AS bajarilmagan
FROM foydalanuvchilar f
LEFT JOIN vazifalar v ON v.egasi_id = f.id
WHERE NOT v.bajarildi
GROUP BY f.id ORDER BY f.id; ism | bajarilmagan
------------------+--------------
Aziz Karimov | 7
Dilnoza Rahimova | 3
Sardor Tursunov | 3
Madina Yusupova | 5
Javohir Olimov | 3
Gulnora Saidova | 2
Bekzod Nazarov | 2
(7 rows)7 qator — Shahnoza yo'q, garchi biz LEFT JOIN yozgan bo'lsak ham. Nima bo'ldi? Tartib bo'yicha kuzatamiz:
| Qadam | Shahnozaning qatori |
|---|---|
1. LEFT JOIN |
qoldi: v.bajarildi = NULL |
2. WHERE NOT v.bajarildi |
NOT NULL → NULL → qator tashlandi |
3. GROUP BY |
Shahnoza yo'q |
WHERE JOIN'dan keyin ishlaydi (SQL tili darsidagi bajarilish tartibi). U o'ng jadval ustunini tekshirganda, LEFT JOIN qo'shgan NULL qatorlar shartdan o'tmaydi. Natijada LEFT JOIN jimgina INNER JOIN'ga aylanadi. Xato xabari yo'q — shuning uchun bu tuzoq xavfli.
4.2 To'g'risi — shart ON da
SELECT f.ism, count(v.id) AS bajarilmagan
FROM foydalanuvchilar f
LEFT JOIN vazifalar v
ON v.egasi_id = f.id AND NOT v.bajarildi
GROUP BY f.id ORDER BY f.id; ism | bajarilmagan
--------------------+--------------
Aziz Karimov | 7
Dilnoza Rahimova | 3
Sardor Tursunov | 3
Madina Yusupova | 5
Javohir Olimov | 3
Gulnora Saidova | 2
Bekzod Nazarov | 2
Shahnoza Ergasheva | 0
(8 rows)Endi shart juftlash qoidasining bir qismi: "bajarilmagan vazifani juft deb ol". Shahnozaga bunday juft topilmadi — LEFT JOIN uni baribir NULL bilan qoldirdi.
Qoida bitta gapda: LEFT JOIN'da o'ng jadvalga oid shart — ON ga, chap jadvalga oid shart — WHERE ga. Masalan WHERE f.id < 5 — xavfsiz, u chap jadvalni filtrlaydi.
Tekshirib ko'ring:
WHERE v.id IS NULLshartli LEFT JOIN nima beradi? Bu ham tuzoqmi?
Javob
Bu tuzoq emas, ataylab ishlatiladigan naqsh: "jufti yo'q qatorlar". foydalanuvchilar LEFT JOIN vazifalar ... WHERE v.id IS NULL — vazifasi yo'q foydalanuvchilar (Shahnoza). IS NULL — NULL qatorlar aynan o'tadigan shart. Bu anti-join deyiladi; uning boshqa yo'lini Subquery va LATERAL darsida ko'ramiz.
5. Ko'p jadvalli zanjir
5.1 Vazifa → bog'lovchi → teg
Teglar vazifa bilan to'g'ridan bog'lanmagan — o'rtada vazifa_teglari turadi. Uchta jadvalni zanjir qilamiz:
SELECT v.id, v.matn, string_agg(t.nom, ' ' ORDER BY t.nom) AS teglar
FROM vazifalar v
JOIN vazifa_teglari vt ON vt.vazifa_id = v.id
JOIN teglar t ON t.id = vt.teg_id
WHERE v.egasi_id = 1 GROUP BY v.id ORDER BY v.id; id | matn | teglar
----+--------------------------------------------------+-----------------
1 | JavaScript kursining 26-qismini boshlash #o'qish | #o'qish
2 | Postgres'ni Docker'da ko'tarish #o'qish #docker | #docker #o'qish
8 | Bozordan kartoshka va piyoz olish #bozor | #bozor
9 | Onamga dori olib borish #oila | #oila
17 | Portfolio saytiga blog yozish #portfolio | #portfolio
23 | Oshxonadagi jo'mrakni almashtirish #ta'mir #uy | #ta'mir #uy
28 | Elektr to'lovini Click orqali qilish #to'lov | #to'lov
34 | SQL JOIN mashqlarini yechish #o'qish | #o'qish
(8 rows)Har JOIN oldingi natijaga yangi jadvalni qo'shadi. Avval vazifa + uning bog'lanishlari, keyin + teg nomi. string_agg bir vazifaning bir nechta tegini bitta satrga yig'adi (Agregatlar). Azizning 12 vazifasidan 8 tasi chiqdi — tegsizlari INNER JOIN'da tushib qoldi.
5.2 Teskari yo'nalish: tegdan odamga
#to'lov tegli vazifalar, kimniki ekani bilan — to'rt jadval:
SELECT f.ism, v.matn, v.bajarildi
FROM teglar t
JOIN vazifa_teglari vt ON vt.teg_id = t.id
JOIN vazifalar v ON v.id = vt.vazifa_id
JOIN foydalanuvchilar f ON f.id = v.egasi_id
WHERE t.nom = '#to''lov' ORDER BY f.ism, v.id; ism | matn | bajarildi
------------------+----------------------------------------------+-----------
Aziz Karimov | Elektr to'lovini Click orqali qilish #to'lov | t
Bekzod Nazarov | Universitet kontrakti #to'lov | f
Dilnoza Rahimova | Gaz to'lovi #to'lov | t
Madina Yusupova | Yotoqxona to'lovi #to'lov | t
Sardor Tursunov | Soliq hisobotini topshirish #ish #to'lov | f
(5 rows)Beshta foydalanuvchida #to'lov bor — har biri o'z tegi (id lari har xil), nomi bir xil. Zanjir qaysi jadvaldan boshlanishi muhim emas: yo'l (FK'lar) bir xil.
5.3 Fan-out: qatorlar ko'payib ketadi
JOIN "bittaga ko'p" tomonga yurganda qatorlar ko'payadi. Bu ba'zan sanashni buzadi. Dilnozada 6 vazifa bor. Lekin teglar bilan birlashtirib sanasak (biz ishga tushirdik, PostgreSQL 17.6):
SELECT f.ism, count(v.id) AS vazifalar
FROM foydalanuvchilar f
JOIN vazifalar v ON v.egasi_id = f.id
JOIN vazifa_teglari vt ON vt.vazifa_id = v.id
WHERE f.id = 2
GROUP BY f.id; ism | vazifalar
------------------+-----------
Dilnoza Rahimova | 7
(1 row)7 — noto'g'ri. "Ingliz tili: 20 ta yangi so'z #o'qish #ingliz" ikki tegli, JOIN'dan keyin u ikki qator bo'ldi va ikki marta sanaldi. Bu hodisa fan-out (yelpig'ich) deyiladi. Tuzatish — takrorsiz sanash va tegsizlarni yo'qotmaslik uchun LEFT JOIN:
SELECT f.ism, count(DISTINCT v.id) AS vazifalar,
count(vt.teg_id) AS boglanishlar
FROM foydalanuvchilar f
JOIN vazifalar v ON v.egasi_id = f.id
LEFT JOIN vazifa_teglari vt ON vt.vazifa_id = v.id
WHERE f.id = 2
GROUP BY f.id; ism | vazifalar | boglanishlar
------------------+-----------+--------------
Dilnoza Rahimova | 6 | 7
(1 row)count(DISTINCT v.id) — har vazifa bir marta. Ikki "ko'p" tomonni (masalan teglar va izohlar) bitta so'rovda birlashtirsangiz, ko'payish ko'paytmaga aylanadi: 3 teg × 4 izoh = 12 qator. Bunday hollarda har birini alohida sanash kerak — Subquery va LATERAL va CTE darslarida.
6. Self-join: jadval o'zi bilan
Kichik vazifaning otasi ham vazifalar jadvalida. Jadvalni o'zi bilan birlashtirish uchun unga ikki xil taxallus beramiz — go'yo ikki nusxa:
SELECT k.id, k.matn AS kichik_vazifa, o.matn AS ota
FROM vazifalar k
JOIN vazifalar o ON o.id = k.ota_id
ORDER BY k.id; id | kichik_vazifa | ota
----+-----------------------------+-----------------------------------------------
41 | Jadvallarni loyihalash | Kurs ishi: ma'lumotlar bazasi bo'limi #o'qish
42 | Adabiyotlar ro'yxati | Kurs ishi: ma'lumotlar bazasi bo'limi #o'qish
43 | Blog uchun mavzu tanlash | Portfolio saytiga blog yozish #portfolio
44 | Qoralama yozish | Portfolio saytiga blog yozish #portfolio
45 | Rasmlarni siqish | Portfolio saytiga blog yozish #portfolio
46 | ER diagramma chizish | Jadvallarni loyihalash
47 | Normalizatsiyani tekshirish | Jadvallarni loyihalash
(7 rows)k — "kichik vazifa" nusxasi, o — "ota" nusxasi. Bu self-join (o'z-o'zi bilan birlashtirish). 46 ning otasi 41, 41 ning otasi 7 — ikki daraja. Bitta JOIN faqat bitta darajani ko'radi. Hamma darajani bitta so'rovda yurish — Rekursiv CTE darsida.
Tekshirib ko'ring: shu so'rovda taxalluslarsiz
FROM vazifalar JOIN vazifalar ON ...deb yozib bo'ladimi?
Javob
Yo'q. Ikki nusxani ajratib bo'lmaydi va Postgres table name "vazifalar" specified more than once xatosini beradi. Self-join'da taxallus majburiy.
7. FULL JOIN, USING va CROSS JOIN
7.1 RIGHT va FULL — vazifasiz teg
Vazifasiz teg yaratamiz (Shahnozaga) va uni RIGHT JOIN bilan topamiz:
INSERT INTO teglar (egasi_id, nom) VALUES (8, '#bosh_teg');
SELECT v.id AS vazifa, t.nom AS teg FROM vazifa_teglari vt
RIGHT JOIN teglar t ON t.id = vt.teg_id
LEFT JOIN vazifalar v ON v.id = vt.vazifa_id WHERE v.id IS NULL;INSERT 0 1
vazifa | teg
--------+-----------
| #bosh_teg
(1 row)RIGHT JOIN teglar — hamma teglar qoladi. WHERE v.id IS NULL — faqat vazifasizlari (anti-join naqshi).
FULL JOIN (to'liq birlashtirish) — ikkala tomonning juftsizlari ham qoladi. Vazifalar va bog'lanishlarni FULL JOIN qilib, uch xil qatorni sanaymiz:
SELECT count(*) FILTER (WHERE v.id IS NULL) AS faqat_teg,
count(*) FILTER (WHERE vt.teg_id IS NULL) AS tegsiz_vazifa,
count(*) AS jami
FROM vazifalar v FULL JOIN vazifa_teglari vt ON vt.vazifa_id = v.id; faqat_teg | tegsiz_vazifa | jami
-----------+---------------+------
0 | 8 | 54
(1 row)8 ta tegsiz vazifa (chap juftsiz), 0 ta vazifasiz bog'lanish (o'ng juftsiz — FK bunga yo'l qo'ymaydi). Jami 54 = 46 juft + 8. FULL JOIN ko'pincha ikki manbani solishtirishda kerak: "eski tizimda bor, yangisida yo'q" va teskarisi.
7.2 USING
Ikki jadvalda juftlash ustuni bir xil nomlansa, ON a.x = b.x o'rniga USING (x):
SELECT vazifa_id, count(*) FROM vazifa_teglari
JOIN (SELECT id AS vazifa_id FROM vazifalar WHERE egasi_id = 2) d
USING (vazifa_id) GROUP BY 1 ORDER BY 1; vazifa_id | count
-----------+-------
5 | 2
14 | 1
16 | 1
26 | 1
29 | 1
36 | 1
(6 rows)USING natijada bitta umumiy vazifa_id ustunini qoldiradi — taxallussiz yozsa bo'ladi. Bizning sxemada nomlar odatda farq qiladi (id va vazifa_id), shuning uchun ON ko'proq ishlatiladi.
NATURAL JOIN — USING ni hamma bir xil nomli ustunlar bo'yicha o'zi tuzadi. Bu xavfli. Biz sinadik (PostgreSQL 17.6):
SELECT f.ism, v.matn
FROM foydalanuvchilar f NATURAL JOIN vazifalar v LIMIT 2; ism | matn
-----+------
(0 rows)0 qator, xatosiz! Ikkala jadvalda id va yaratilgan_vaqt bor — NATURAL ularni tenglashtirdi: "foydalanuvchi id si = vazifa id si VA vaqtlar teng". Ertaga jadvalga bir xil nomli ustun qo'shilsa, so'rov ma'nosi jimgina o'zgaradi. NATURAL JOIN'ni ishlatmang.
7.3 CROSS JOIN
CROSS JOIN — shartsiz, har qator har qator bilan (dekart ko'paytma):
SELECT count(*) FROM foydalanuvchilar
CROSS JOIN unnest(enum_range(NULL::ustuvorlik)) u; count
-------
24
(1 row)8 foydalanuvchi × 3 ustuvorlik = 24. Foydali joyi — hisobot "katagi": har foydalanuvchi uchun har ustuvorlik qatori bo'lsin, keyin LEFT JOIN bilan sonlar qo'yilsin (nollar ham ko'rinadi). enum_range — enum qiymatlari massivi, unnest uni qatorlarga yoyadi.
Xavfli tomoni: ON unutilsa, eski vergulli yozuv ham dekart ko'paytma beradi:
SELECT count(*) AS dekart FROM foydalanuvchilar, vazifalar; dekart
--------
376
(1 row)8 × 47 = 376. Shu 8 foydalanuvchi va 2 mln vazifada bu 16 mln qator bo'lardi; kanonning katta bazasida (20 000 foydalanuvchi × 2 mln vazifa) esa 40 mlrd. Vergulli yozuv (FROM a, b WHERE a.x = b.x) eski kodda uchraydi — ishlaydi, lekin shartni unutish oson. Doim JOIN ... ON yozing.
8. Ko'p uchraydigan xatolar
8.1 column reference "id" is ambiguous
SELECT id, matn FROM vazifalar v
JOIN foydalanuvchilar f ON f.id = v.egasi_id;ERROR: column reference "id" is ambiguous
LINE 1: SELECT id, matn FROM vazifalar v
^Tarjimasi: "id ustuniga havola ikki ma'noli". Ikkala jadvalda ham id bor. Yechim — v.id yoki f.id. Odat qiling: JOIN'li so'rovda har ustunni taxallus bilan yozing, hatto yagona bo'lsa ham.
8.2 ON siz JOIN
SELECT * FROM vazifalar v JOIN foydalanuvchilar f;ERROR: syntax error at or near ";"
LINE 1: SELECT * FROM vazifalar v JOIN foydalanuvchilar f;
^JOIN (INNER, LEFT, RIGHT, FULL) shartsiz bo'lmaydi — Postgres ON yoki USING ni kutgan joyda ; ni ko'rdi. Faqat CROSS JOIN shartsiz.
8.3 LEFT JOIN Shahnozani "yo'qotdi"
O'ng jadval sharti WHERE da — «LEFT JOIN + WHERE tuzog'i» bo'limi. Shartni ON ga ko'chiring.
8.4 Son kutilganidan katta
Fan-out: bir nechta "ko'p" jadval birlashtirilgan. count(DISTINCT ...) yoki alohida sanash.
8.5 count(*) LEFT JOIN'da 0 o'rniga 1 berdi
Juftsiz qator ham bitta qator. O'ng jadval ustunini sanang: count(v.id).
9. Mashqlar
Mashq fayllari kurs/mashqlar/26/24-join/ da. Hammasi vazifalar holat 23 da.
1-mashq (oson): kichik vazifalar egasi
Hamma kichik vazifalarni (ota_id IS NOT NULL) egasining ismi bilan chiqaring: id, matn, ism, id bo'yicha tartiblang. Yordam: «INNER JOIN» bo'limidagi kabi JOIN foydalanuvchilar f ON f.id = v.egasi_id.
Yechim
SELECT v.id, v.matn, f.ism
FROM vazifalar v
JOIN foydalanuvchilar f ON f.id = v.egasi_id
WHERE v.ota_id IS NOT NULL
ORDER BY v.id; id | matn | ism
----+-----------------------------+-----------------
41 | Jadvallarni loyihalash | Madina Yusupova
42 | Adabiyotlar ro'yxati | Madina Yusupova
43 | Blog uchun mavzu tanlash | Aziz Karimov
44 | Qoralama yozish | Aziz Karimov
45 | Rasmlarni siqish | Aziz Karimov
46 | ER diagramma chizish | Madina Yusupova
47 | Normalizatsiyani tekshirish | Madina Yusupova
(7 rows)WHERE v.ota_id IS NOT NULL — chap jadval (vazifalar) sharti, INNER JOIN'da WHERE ga yozish to'g'ri.
2-mashq (o'rta): yuqori ustuvorlikdagilar, nol bilan
Har foydalanuvchida nechta yuqori ustuvorlikdagi vazifa borligini chiqaring — Shahnoza ham 0 bilan ko'rinsin. Ustunlar: ism, yuqori; id bo'yicha. Yordam: shart qayerga yoziladi — «LEFT JOIN + WHERE tuzog'i».
Yechim
SELECT f.ism, count(v.id) AS yuqori
FROM foydalanuvchilar f
LEFT JOIN vazifalar v
ON v.egasi_id = f.id AND v.ustuvorlik = 'yuqori'
GROUP BY f.id ORDER BY f.id; ism | yuqori
--------------------+--------
Aziz Karimov | 3
Dilnoza Rahimova | 1
Sardor Tursunov | 2
Madina Yusupova | 1
Javohir Olimov | 1
Gulnora Saidova | 1
Bekzod Nazarov | 1
Shahnoza Ergasheva | 0
(8 rows)v.ustuvorlik = 'yuqori' — o'ng jadval sharti, shuning uchun ON da. WHERE da bo'lsa Shahnoza tushib qolardi.
3-mashq (qiyin): teglar soni, fan-out'siz
Har foydalanuvchi uchun: ism, vazifalar soni, turli teglar soni (bir nechta vazifada ishlatilgan teg bir marta). Teglari yo'q va vazifasi yo'q foydalanuvchi ham 0 bilan chiqsin. Yordam: ikki LEFT JOIN (vazifalar, vazifa_teglari) va count(DISTINCT ...).
Yechim
SELECT f.ism,
count(DISTINCT v.id) AS vazifalar,
count(DISTINCT vt.teg_id) AS teglar
FROM foydalanuvchilar f
LEFT JOIN vazifalar v ON v.egasi_id = f.id
LEFT JOIN vazifa_teglari vt ON vt.vazifa_id = v.id
GROUP BY f.id ORDER BY f.id; ism | vazifalar | teglar
--------------------+-----------+--------
Aziz Karimov | 12 | 8
Dilnoza Rahimova | 6 | 6
Sardor Tursunov | 6 | 5
Madina Yusupova | 9 | 3
Javohir Olimov | 6 | 3
Gulnora Saidova | 4 | 3
Bekzod Nazarov | 4 | 4
Shahnoza Ergasheva | 0 | 0
(8 rows)Ikkinchi LEFT JOIN ham LEFT bo'lishi shart: aks holda tegsiz vazifalar va Shahnoza tushib qoladi. DISTINCT siz vazifalar soni fan-out bilan oshib ketardi.
4-mashq: SQL qadami — ro'yxat so'rovi teglar bilan
25-qism API'si vazifalar ro'yxatini teglar massivi bilan qaytaradi. O'z bazangizda Azizning (egasi_id = 1) oxirgi 4 vazifasini teglar massivi bilan chiqaring; tegsiz vazifada bo'sh massiv {} bo'lsin. Sxema o'zgarmaydi — commit yo'q, so'rovni eslatma sifatida saqlang. Yordam: array_agg(... ) FILTER (WHERE ...) va coalesce (Agregatlar, NULL mantiqi).
Yechim
SELECT v.id, v.matn,
coalesce(array_agg(t.nom ORDER BY t.nom)
FILTER (WHERE t.nom IS NOT NULL), '{}') AS teglar
FROM vazifalar v
LEFT JOIN vazifa_teglari vt ON vt.vazifa_id = v.id
LEFT JOIN teglar t ON t.id = vt.teg_id
WHERE v.egasi_id = 1
GROUP BY v.id
ORDER BY v.yaratilgan_vaqt DESC, v.id DESC
LIMIT 4; id | matn | teglar
----+------------------------------------------------+---------------
38 | Velosiped zanjirini moylash | {}
34 | SQL JOIN mashqlarini yechish #o'qish | {#o'qish}
28 | Elektr to'lovini Click orqali qilish #to'lov | {#to'lov}
23 | Oshxonadagi jo'mrakni almashtirish #ta'mir #uy | {#ta'mir,#uy}
(4 rows)FILTER (WHERE t.nom IS NOT NULL) siz tegsiz vazifada {NULL} chiqardi (LEFT JOIN'ning NULL qatori). Barcha qatorlar filtrlansa array_agg NULL beradi — coalesce uni {} ga almashtiradi. JavaScript'ga bu bo'sh massiv bo'lib keladi.
10. Real ishda
- Har API endpoint ichida JOIN bor: buyurtma + mijoz + mahsulotlar, post + muallif + izohlar soni. ORM'lar (Prisma
include, Drizzlewith) ham ichida JOIN yoki alohida so'rovlar yasaydi (27-qism). - LEFT JOIN + WHERE — kod review'da eng ko'p topiladigan SQL xatolaridan biri: hisobotda "nol"lar yo'qoladi va hech kim sezmaydi.
- Fan-out — dashboard raqamlari "ikki baravar" chiqishining odatiy sababi.
- Tezlik: JOIN ustunlari (FK) indeksli bo'lsin — Foreign key darsida
vazifalar_egasi_id_idxni shuning uchun qo'shdik. JOIN algoritmlari (nested loop, hash, merge) — EXPLAIN ANALYZE'ni o'qish darsida. - Intervyu: "INNER va LEFT farqi", "LEFT JOIN'da shart ON yoki WHERE'da — farqi nima?" — eng ko'p beriladigan SQL savollari.
Xulosa
JOIN ... ON— ikki jadval qatorlarini shart bo'yicha juftlaydi; har ustunni taxallus bilan yozing.- INNER — faqat juftlilar; LEFT — chapning hammasi (
NULLbilan); FULL — ikkala tomonning hammasi; RIGHT — LEFT'ning aksi. - LEFT JOIN'da o'ng jadval sharti
ONga;WHEREga yozilsa, LEFT jimgina INNER bo'ladi. - LEFT JOIN'dan keyin
count(o'ng.id)sanang; ko'p tomonli JOIN'da fan-out'dan ehtiyot bo'ling (count(DISTINCT)). - Self-join — bitta jadval, ikki taxallus; CROSS JOIN — har kombinatsiya; NATURAL JOIN — ishlatmang.
Keyingi dars: Subquery va LATERAL — so'rov ichida so'rov va "har foydalanuvchining oxirgi 2 vazifasi".
Manbalar
- PostgreSQL 18 hujjati — Joins Between Tables: https://www.postgresql.org/docs/18/tutorial-join.html
- PostgreSQL 18 hujjati — Table Expressions, Joined Tables: https://www.postgresql.org/docs/18/queries-table-expressions.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!