Ачааллж байна...
Өмнөх хичээлийн бүх INNER JOIN-д нэг хүн тууштай алга байсан: Ану. Тэр номын сангийн гишүүн, имэйл хаягтай, 2025 онд элссэн — гэвч ганц ч ном зээлээгүй тул зээлийн хүснэгтэд түүн рүү заасан мөр байхгүй. INNER JOIN тааралдсан хосуудыг л үлдээдэг тул Ану хос олдоогүй мөр болон чимээгүйхэн хасагдсан. Номын тал дээр ч мөн адил: "Монголын нууц товчоо", "Учиртай гурван толгой", “Clean Architecture” хэзээ ч зээлэгдээгүй тул ном-зээлийн INNER JOIN-д тэдгээр хэзээ ч гарч ирэхгүй.
JOIN гэж юу вэ хичээлд бид энэ зан чанар заримдаа зөв, заримдаа ноцтой алдаа гэж ярилцсан. "Хэн юу зээлсэн бэ" гэсэн асуултад Ану хамаагүй. Харин "хэзээ ч зээлээгүй гишүүдэд урамшуулал илгээе" гэсэн асуултад тэр цорын ганц хариулт бөгөөд INNER JOIN түүнийг яг хамгийн хэрэгтэй мөчид нууна. Энэ хичээлд бид тааралгүй мөрүүдийг хадгалдаг JOIN — LEFT JOIN-ийг сурна.
LEFT JOIN-ийн бичлэг INNER JOIN-оос нэг л үгээр ялгаатай боловч зан чанар нь хоёр чухал зүйлээр ялгаатай: хүснэгтүүдийн дараалал чухал болно, мөн шүүлтийн нөхцлийг ON-д эсвэл WHERE-д бичих нь огт өөр үр дүн өгнө. Хоёуланг нь ижил өгөгдөл дээр INNER JOIN-тэй зэрэгцүүлэн харж, өмнөх хичээлд "дараагийн хичээлд огт өөр утгатай болно" гэж анхааруулсан урхийг өөрийн нүдээр олно. library2.db-г нээгээрэй — энэ хичээлд бид зөвхөн уншина.
Номын санч гишүүдийн дэвтэр ба зээлийн дэвтрийг зэрэгцүүлэн тавьсан. INNER JOIN-ийн үед түүний заавар нь "тааралдсан хос бүрийг цаасан дээр бич" байсан — тааралгүй гишүүн цаасан дээр хэзээ ч гарахгүй. LEFT JOIN-ийн заавар өөр: "гишүүдийн дэвтрийн мөр бүрийг заавал цаасан дээр бич. Хэрэв түүний зээл байвал зээл бүрийг хажууд нь бич. Хэрэв нэг ч зээл байхгүй бол гишүүнийг бичээд, зээлийн нүднүүдийг хоосон үлдээ."
Энэ зааварт нэг дэвтэр "гол" дэвтэр болсныг анзаараарай — гишүүдийн дэвтэр. Түүний мөр бүр үр дүнд баталгаатай гарна. Нөгөө дэвтэр (зээлийн) "нэмэлт" — тааралдвал мэдээллээ өгнө, тааралдахгүй бол хоосон нүд үлдээнэ. SQL-д "гол" дэвтэр нь FROM-д бичигдсэн, зүүн талд байгаа хүснэгт тул энэ JOIN-ийг LEFT (зүүн) гэж нэрлэдэг. Хоосон нүд нь Бүлэг 2-ын NULL — "энд утга байхгүй."
Бидний хоёр хүснэгт:
members loans
id | name id | member_id | loaned_on
---+--------- ---+-----------+-----------
1 | Бат 1 | 1 | 2026-09-03
2 | Сараа 2 | 1 | 2026-09-10
3 | Тэмүүлэн 3 | 2 | 2026-09-12
4 | Номин 4 | 1 | 2026-09-20
5 | Ану 5 | 4 | 2026-09-25
6 | 2 | 2026-10-01
7 | 3 | 2026-10-03
8 | 4 | 2026-10-05Query:
sql
SELECT m.name, l.id AS loan_id, l.loaned_on
FROM members AS m
LEFT JOIN loans AS l ON l.member_id = m.id
ORDER BY m.name, l.loaned_on;Үр дүн:
name | loan_id | loaned_on
---------+---------+-----------
Ану | NULL | NULL
Бат | 1 | 2026-09-03
Бат | 2 | 2026-09-10
Бат | 4 | 2026-09-20
Номин | 5 | 2026-09-25
Номин | 8 | 2026-10-05
Сараа | 3 | 2026-09-12
Сараа | 6 | 2026-10-01
Тэмүүлэн | 7 | 2026-10-03Энэ query юу гэж хэлж байна вэ гэвэл: “members-ийн бүх мөрийг аваад, гишүүн бүрт түүний member_id-тай тааралдах зээлүүдийг нийлүүл; тааралдах зээлгүй гишүүнийг ч зээлийн нүднүүдийг хоосон болгон үлдээ; гишүүний нэр, зээлийн дугаар, огноог нэр болон огноогоор эрэмбэлж харуул.”
Бичлэгийн хувьд ганц өөрчлөлт: INNER JOIN-ийн оронд LEFT JOIN. ON, alias, SELECT, ORDER BY бүгд өмнөх хичээлтэй ижил. Мөр бүрийн хувь заяаг мөшгье. Бат гурван зээлтэй тул гурван мөр. Номин, Сараа тус бүр хоёр зээлтэй тул хоёр мөр. Тэмүүлэн нэг зээлтэй тул нэг мөр. Энэ хүртэл INNER JOIN-той яг адил. Харин Ану — тааралдах ганц ч зээлгүй. INNER JOIN түүнийг хасах байсан бол LEFT JOIN түүнийг "гол" хүснэгтийн мөр гэж хадгалж, loans-ийн бүх баганыг NULL болгожээ. Үр дүн есөн мөр: найман тааралдсан хос дээр нэмэх нь нэг тааралгүй гишүүн.
Ану жагсаалтын эхэнд гарсан нь Бүлэг 2-ын эрэмбэлэлтийн хичээлийн дагуу "А" үсэг кирилл цагаан толгойн эхэнд байдагтай холбоотой. Мессежийн талбарын 9 rows returned нь бидний таамаглал (8 + 1) -тэй таарч байна.
Жишээ 1: INNER ба LEFT зэрэгцүүлэн
Ижил хоёр хүснэгт дээр хоёр JOIN-ийн ялгааг тод харахын тулд үр дүнг зэрэгцүүлье. Өмнөх хичээлийн Жишээ 4 (INNER JOIN, members-ээс эхэлсэн) ба энэ хичээлийн Анхны жишээ:
INNER JOIN (8 мөр) LEFT JOIN (9 мөр)
name | loan_id name | loan_id
---------+-------- ---------+--------
Ану | NULL
Бат | 1 Бат | 1
Бат | 2 Бат | 2
Бат | 4 Бат | 4
Номин | 5 Номин | 5
Номин | 8 Номин | 8
Сараа | 3 Сараа | 3
Сараа | 6 Сараа | 6
Тэмүүлэн | 7 Тэмүүлэн | 7Тааралдсан найман хос хоёуланд нь яг ижил. Цорын ганц ялгаа нь Анугийн мөр. Өөрөөр хэлбэл LEFT JOIN = INNER JOIN-ийн бүх мөр + зүүн хүснэгтийн тааралгүй мөрүүд (баруун талын нүднүүд нь NULL). Энэ томъёо LEFT JOIN-ийг ойлгох хамгийн найдвартай арга. JOIN гэж юу вэ хичээлийн Venn диаграмын санамжаар бол: INNER нь давхцсан хэсэг, LEFT нь зүүн тойрог бүхэлдээ — гэхдээ мөрийн тоог Бат гурван удаа гарсныг харж, хосын загвараар тоолно.
Жишээ 2: хэзээ ч зээлээгүй гишүүд — сонгодог асуулт
Одоо JOIN гэж юу вэ хичээлд дэвшүүлсэн асуултыг нэг query-ээр шийдье: хэн хэзээ ч ном зээлээгүй вэ? Анхны жишээний үр дүнг харвал хариулт тодорхой — loans-ийн нүд нь NULL байгаа мөр. Тэгэхээр LEFT JOIN-ийн дараа тэр мөрүүдийг л үлдээе. Query:
sql
SELECT m.name, m.email
FROM members AS m
LEFT JOIN loans AS l ON l.member_id = m.id
WHERE l.id IS NULL;Үр дүн:
name | email
-----+----------------
Ану | anu@example.comЭнэ query юу гэж хэлж байна вэ гэвэл: “members-ийн бүх мөрийг loans-тай нийлүүлж, тааралгүйг нь ч хадгал; дараа нь зээлийн id нь хоосон — өөрөөр хэлбэл ямар ч зээл тааралдаагүй — гишүүдийн нэр, имэйлийг харуул.” Гүйцэтгэх дарааллаар нь бодвол: эхлээд LEFT JOIN есөн мөр үүсгэнэ (Анхны жишээ), дараа нь WHERE тэдгээрээс loans-ийн id нь NULL байгаа мөрийг үлдээнэ — зөвхөн Ану. Энэ “LEFT JOIN ... WHERE баруун_хүснэгт.id IS NULL” хэлбэр нь "А-д байгаа, гэхдээ Б-д тохирох зүйлгүй"-г олох стандарт загвар бөгөөд олон бодит асуултын хариулт: хэзээ ч худалдан авалт хийгээгүй үйлчлүүлэгчид, хэзээ ч нэвтрээгүй хэрэглэгчид, сэтгэгдэлгүй бүтээгдэхүүнүүд.
Бид яагаад l.id-г шалгасан бэ? Учир нь loans-ийн PRIMARY KEY жинхэнэ зээлд хэзээ ч NULL байдаггүй — Бүлэг 4-т сурсанчлан мөр бүр дугаартай. Тиймээс энэ багана NULL байвал энэ нь "зээл байхгүй" гэдгийн цорын ганц шалтгаан. Хэрэв бид NULL байж болох өөр баганыг сонгосон бол юу болохыг Түгээмэл алдаанууд хэсэгт харна.
Жишээ 3: хүснэгтүүдийн дараалал чухал
Өмнөх хичээлд INNER JOIN тэгш хэмтэй гэж сурсан. LEFT JOIN тийм биш. Хүснэгтүүдийн байрыг сольё. Query:
sql
SELECT l.id AS loan_id, m.name
FROM loans AS l
LEFT JOIN members AS m ON l.member_id = m.id
ORDER BY l.id;Үр дүн:
loan_id | name
--------+---------
1 | Бат
2 | Бат
3 | Сараа
4 | Бат
5 | Номин
6 | Сараа
7 | Тэмүүлэн
8 | НоминЭнэ query юу гэж хэлж байна вэ гэвэл: “loans-ийн бүх мөрийг аваад, зээл бүрт тааралдах гишүүнийг нийлүүл; тааралдахгүй зээлийг ч хадгал.” Ану дахин алга боллоо. Учир нь одоо "гол" хүснэгт нь loans — зээл бүр баталгаатай гарна, харин гишүүд зөвхөн нэмэлт мэдээлэл. Ану ямар ч зээлтэй тааралдаагүй тул түүнд "гол" хүснэгтээс орох эрх байхгүй.
Энэ query-ний үр дүн INNER JOIN-тэй яг ижил найман мөр боллоо. Шалтгаан нь Бүлэг 5-ийн FOREIGN KEY: зээл бүрийн member_id заавал байх (NOT NULL) бөгөөд байгаа гишүүн рүү заах ёстой — тааралгүй зээл байх боломжгүй. Тиймээс энэ чиглэлд LEFT JOIN нь INNER JOIN-оос юу ч нэмэхгүй. Дүрэм: LEFT JOIN-ийг бичихээсээ өмнө "аль хүснэгтийн бүх мөрийг хадгалах ёстой вэ" гэж асуу — тэр хүснэгт FROM-д, зүүн талд бичигдэнэ.
Жишээ 4: хэзээ ч зээлэгдээгүй номнууд
Ижил загварыг номын тал дээр хэрэглэе. Хэзээ ч хэн ч зээлээгүй номнууд аль нь вэ? Үүнийг мэдвэл номын сан тэдгээрийг сурталчлах эсвэл дараагийн худалдан авалтаа өөрөөр төлөвлөх боломжтой. Query:
sql
SELECT b.id, b.title, b.genre
FROM books AS b
LEFT JOIN loans AS l ON l.book_id = b.id
WHERE l.id IS NULL
ORDER BY b.id;Үр дүн:
id | title | genre
---+-----------------------+------------
1 | Монголын нууц товчоо | Түүх
3 | Учиртай гурван толгой | Жүжиг
7 | Clean Architecture | ПрограмчлалЭнэ query юу гэж хэлж байна вэ гэвэл: “books-ийн бүх мөрийг loans-тай номын id-аар нийлүүлж, тааралгүйг нь ч хадгал; дараа нь ямар ч зээл тааралдаагүй номнуудын id, нэр, жанрыг харуул.” Түвшин 2-ын удирдамжтай дасгалд бид энэ гурван номыг санаатайгаар хэзээ ч зээлэгдээгүй байхаар өгөгдлөө бэлдсэн — одоо тэд бидний query-ний хариулт болж гарч ирлээ. Жишээ 2-той бүтэц нь яг ижил: зөвхөн хүснэгтийн нэр, тааруулах багана өөрчлөгдсөн. Энэ загварыг нэг удаа ойлговол аль ч хүснэгтийн хос дээр давтаж болно.
Жишээ 5: ON уу, WHERE үү — LEFT JOIN-ийн урхи
Өмнөх хичээлийн Түгээмэл алдаанууд хэсэгт “INNER JOIN-д ON ба WHERE ижил шүүдэг, гэвч LEFT JOIN-д огт өөр утгатай болно” гэж анхааруулсан. Одоо тэр мөч ирлээ. Эрхлэгч "гишүүн бүрийг жагсаа, хажууд нь одоо гарт байгаа (буцаагаагүй) зээлийг бич; гарт ном байхгүй гишүүдийг ч жагсаалтад үлдээ" гэж хүсчээ. Таван гишүүн, таван мөр (эсвэл илүү) хүлээгдэж байна. Эхний оролдлого — шүүлтийг WHERE-д:
sql
SELECT m.name, l.id AS loan_id
FROM members AS m
LEFT JOIN loans AS l ON l.member_id = m.id
WHERE l.returned_on IS NULL
ORDER BY m.name;Үр дүн:
name | loan_id
---------+--------
Ану | NULL
Бат | 4
Сараа | 6
Тэмүүлэн | 7Энэ query юу гэж хэлж байна вэ гэвэл: "бүх гишүүдийг зээлүүдтэй нь нийлүүл, тааралгүйг нь хадгал; дараа нь буцаасан огноо нь хоосон мөрүүдийг л үлдээ." Дөрвөн мөр — Номин алга! Яагаад? Гүйцэтгэх дарааллыг мөшгье. LEFT JOIN эхлээд Анхны жишээний есөн мөрийг үүсгэнэ. Номины хоёр мөр (зээл 5 ба 8) хоёулаа буцаагдсан — returned_on нь огноотой. Дараа нь WHERE эдгээр хоёр мөрийг хоёуланг нь хасна. Номинд үлдэх мөр алга. LEFT JOIN Номиныг хадгалсан ч WHERE түүнийг дараа нь устгажээ. Анугийн мөр санамсаргүй үлдсэн: түүний returned_on нь LEFT JOIN-оос ирсэн NULL тул шалгалтыг давсан — гэвч "буцаагаагүй зээл"-ийн улмаас биш, "огт зээлгүй"-н улмаас.
Одоо шүүлтийг ON-д шилжүүлье:
sql
SELECT m.name, l.id AS loan_id
FROM members AS m
LEFT JOIN loans AS l ON l.member_id = m.id AND l.returned_on IS NULL
ORDER BY m.name;Үр дүн:
name | loan_id
---------+--------
Ану | NULL
Бат | 4
Номин | NULL
Сараа | 6
Тэмүүлэн | 7Энэ query юу гэж хэлж байна вэ гэвэл: "бүх гишүүдийг аваад, гишүүн бүрт түүний буцаагаагүй зээлүүдийг л нийлүүл; тааралдах буцаагаагүй зээлгүй гишүүнийг хоосон нүдтэйгээр хадгал." Таван мөр — бүх гишүүн. Номин одоо байна, хажууд нь NULL: "одоо гарт ном байхгүй." Ялгаа нь шүүлт хэзээ ажилласанд оршино. ON дахь нөхцөл "аль зээлүүд тааралдах вэ"-г шийддэг — Номины зээлүүд тааралдаагүй тул LEFT JOIN түүнийг тааралгүй гишүүн гэж хадгалсан. WHERE дахь нөхцөл "нийлсэн мөрүүдээс алийг нь үлдээх вэ"-г шийддэг — тэр нь LEFT JOIN хадгалсан мөрүүдийг ч устгаж чадна.
Дүрэм нэг өгүүлбэрт: LEFT JOIN-д баруун хүснэгтийн баганын нөхцлийг ON-д бичвэл "аль мөрүүд тааралдах вэ"-г, WHERE-д бичвэл "аль мөрүүд үлдэх вэ"-г шийднэ. Хэрэв зүүн хүснэгтийн бүх мөрийг хадгалах нь зорилго бол баруун талын шүүлтийг ON-д бич. Жишээ 2-ын WHERE l.id IS NULL бол санаатай онцгой тохиолдол — тэнд бид тааралгүй мөрүүдийг л үлдээхийг яг хүссэн. INNER JOIN-д энэ ялгаа байдаггүй, учир нь тааралгүй мөр хэзээ ч үлддэггүй.
Хэрэв LEFT JOIN байгаа бол RIGHT JOIN ч байх уу гэж асууж болно. Байдаг — баруун талын хүснэгтийн бүх мөрийг хадгалдаг JOIN. Гэвч A RIGHT JOIN B нь B LEFT JOIN A-тай яг ижил, зөвхөн бичлэг нь урвуу. Ихэнх мэргэжилтнүүд үргэлж LEFT JOIN бичиж, хадгалах хүснэгтээ FROM-д тавьдаг — ингэснээр query-г уншихад "гол" хүснэгт үргэлж эхэнд байна. Мөн FULL JOIN гэж хоёр талын тааралгүй мөрүүдийг хоёуланг нь хадгалдаг JOIN бий бөгөөд энэ нь ховор, ихэвчлэн хоёр өгөгдлийн эх сурвалжийг хооронд нь тулгаж шалгахад хэрэглэгддэг. SQLite-ийн шинэ хувилбарууд болон PostgreSQL хоёулаа хоёуланг нь дэмждэг. Энэ сургалтад бид LEFT JOIN-ийг л ашиглана.
Хэрэв сонирхож байвал хэзээ ч ном бичээгүй (номын санд номгүй) зохиогчдыг олж үзээрэй — authors ба book_authors хүснэгтүүд дээр Жишээ 2-ын загвараар. Хариулт нь нэг зохиогч байх ёстой. Мөн эсрэг асуулт: зохиогчийн холбоосгүй номнууд аль нь вэ? (books ба book_authors). Мөн Жишээ 5-ын хоёр query-г өөрийн гараар бичиж, мөрийн тоог ажиллуулахаасаа өмнө таамаглаарай. Эдгээр нь зөвхөн санал бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж тооцохгүй.
Тааралгүй мөрийг NULL байж болох баганаар шалгах. "Хэзээ ч зээлээгүй гишүүд"-ийг олохдоо WHERE l.returned_on IS NULL гэж бичвэл Ану гарч ирэх боловч Бат (зээл 4), Сараа (зээл 6), Тэмүүлэн (зээл 7) ч мөн гарч ирнэ — тэдний буцаагаагүй зээлүүдийн returned_on жинхэнэ NULL. Алдааны мессеж гарахгүй, үр дүнд илүү хүмүүс орно. Тааралгүй мөрийг шалгахдаа баруун хүснэгтийн хэзээ ч NULL байдаггүй баганыг — ихэвчлэн PRIMARY KEY-г — ашиглаарай.
Баруун хүснэгтийн шүүлтийг WHERE-д бичих. Жишээ 5-д харсанчлан энэ нь LEFT JOIN-ийн хадгалсан мөрүүдийг устгаж, үр дүнг INNER JOIN-тэй адилхан болгож чадна. Алдааны мессеж гарахгүй. Хэрэв LEFT JOIN-ийн үр дүнд хүлээгдэж байсан мөр алга бол WHERE дахь баруун хүснэгтийн нөхцлийг ON руу шилжүүлж үзээрэй.
Хүснэгтүүдийн дарааллыг буруу сонгох. Жишээ 3-т харсанчлан loans LEFT JOIN members нь тааралгүй гишүүдийг хадгалахгүй. LEFT JOIN-ийн дараа хүлээгдэж байсан тааралгүй мөр гарахгүй бол FROM-д ямар хүснэгт байгааг шалгаарай: хадгалах ёстой хүснэгт зүүн талд байх ёстой.
LEFT JOIN-ийн дараа мөр тоолох. Хэрэв Анхны жишээний дараа "хэдэн зээл байна вэ" гэж мөр тоолбол 9 гэсэн хариу авна — гэвч зээл найм. Анугийн мөр зээл биш, харин "зээлгүй гишүүн"-ийн мөр. Энэ нь Бүлэг 8-д COUNT-ийг сурахад маш чухал: COUNT(*) нь бүх мөрийг тоолдог бол COUNT(l.id) нь зөвхөн NULL биш утгуудыг тоолдог. Тэр ялгааг дараагийн бүлгийн эхний хичээлд нарийвчлан харна.
NULL-ийг =-ээр шалгах. WHERE l.id = NULL гэж бичвэл Бүлэг 2-ын NULL-ийн хичээлийн урхи давтагдана — нэг ч мөр гарахгүй. Тааралгүй мөрийг үргэлж IS NULL-ээр шалгана.
Энэ хичээлд та LEFT JOIN-ийг сурлаа. LEFT JOIN нь FROM-д бичигдсэн зүүн хүснэгтийн бүх мөрийг хадгалж, тааралдах мөргүй үед баруун хүснэгтийн бүх баганыг NULL болгодог — өөрөөр хэлбэл INNER JOIN-ийн бүх мөр дээр нэмэх нь зүүн талын тааралгүй мөрүүд. LEFT JOIN ... WHERE баруун.id IS NULL загвар нь "хэзээ ч зээлээгүй гишүүд", "хэзээ ч зээлэгдээгүй номнууд" гэх мэт байхгүй зүйлийг олох стандарт арга бөгөөд шалгалтыг хэзээ ч NULL байдаггүй баганаар (PRIMARY KEY) хийнэ. INNER JOIN-оос ялгаатай нь LEFT JOIN-д хүснэгтүүдийн дараалал чухал, мөн баруун хүснэгтийн нөхцлийг ON-д бичих нь "аль мөр тааралдах"-ыг, WHERE-д бичих нь "аль мөр үлдэх"-ийг шийддэг тул огт өөр үр дүн гаргана. RIGHT JOIN нь урвуу бичсэн LEFT JOIN тул бид LEFT-ийг тууштай ашиглана.
Дараагийн хичээлээр Бүлэг 7 дуусна. Одоо хүртэл бид хоёр хүснэгтийг нийлүүлсэн. Түвшин 2-ын удирдамжтай дасгалын Алхам 7-ын “2-р номыг 1-р гишүүн” гэсэн асуудлыг бүрэн шийдэхийн тулд гурван хүснэгт хэрэгтэй: зээл, гишүүн, ном. Бид зээлийн тайланг алхам алхмаар — эхлээд хоёр хүснэгт, дараа нь гурав — бүтээж, мөн олон-олон харилцааны холбогч хүснэгтээр дамжин уншиж, Бүлэг 5-ийн урьдчилсан харагдацын query-г мөр бүрээр нь бүрэн ойлгоно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.