Ачааллж байна...
Өмнөх хичээлд бид JOIN-ийг цаасан дээр, гараараа хийсэн: зээлийн мөр бүрийн member_id-г гишүүдийн хүснэгтэд хайж, тааралдсан хос бүрийг шинэ цаасан дээр нэг мөр болгон бичсэн. Мөн JOIN-ийг "бүх боломжит хосоос тааруулах дүрэмд нийцсэнийг үлдээх" гэж ойлгож, тааралгүй мөрүүд хоёр өөр хувь заяатай байж болдгийг харсан. Энэ хичээлд тэр ажлыг өгөгдлийн сан хийх болно — бид зөвхөн юуг, ямар дүрмээр нийлүүлэхийг хэлнэ.
Бид INNER JOIN — зөвхөн тааралдсан хосуудыг үлдээдэг JOIN-ийн бичлэгийг сурна. Тааруулах дүрмийг илэрхийлэх ON хэсэг, хоёр хүснэгтэд ижил нэртэй баганыг ялгах арга, урт хүснэгтийн нэрийг богиносгох alias-ийг сурна. Query бүрийг Бүлэг 1-ээс хойшхи дадлаараа монголоор чангаар уншиж, үр дүнгийн мөр бүрийг хаанаас ирснийг мөшгинө.
Хичээлийн төгсгөлд өмнөх хичээлд анхааруулсан хоёр аюулыг бодитоор үзнэ: тааруулах дүрмээ мартахад мөрүүд хэрхэн "тэсрэлт" хийдэг, буруу баганыг тааруулахад алдааны мессежгүй худал үр дүн хэрхэн гардаг. Энэ хичээлээс эхлэн бүх query library2.db дээр ажиллана. DB Browser-оор тэр файлыг нээгээрэй. Бид энэ хичээлд зөвхөн уншина, тиймээс Write Changes, Revert Changes аль аль нь шаардлагагүй.
Өмнөх хичээлийн номын санчийг дахин санаарай. Эрхлэгч түүнд хоёр дэвтэр өгөөд хэлэх зааварт гурван хэсэг бий: "Зээлийн дэвтрийг ав" (эхний дэвтэр), "гишүүдийн дэвтэртэй зэрэгцүүл" (хоёр дахь дэвтэр), "зээлийн гишүүний дугаарыг гишүүдийн дугаартай тааруул" (тааруулах дүрэм). INNER JOIN-ийн бичлэг яг энэ гурван хэсгийг SQL-ээр хэлдэг: FROM эхний хүснэгт, JOIN хоёр дахь хүснэгт, ON тааруулах дүрэм.
Номын санч ажлынхаа үеэр "дугаар" гэсэн үгийг хоёр дэвтэрт хоёуланд нь хардаг — зээл бүр өөрийн дугаартай, гишүүн бүр өөрийн дугаартай. Эрхлэгч "дугаарыг нь бич" гэвэл "аль дэвтрийн дугаарыг?" гэж асуух нь зүйн хэрэг. SQL-д ч ижил асуудал гарна, шийдэл нь мөн ижил: багана бүрийг аль хүснэгтийнх болохыг нь хэлэх.
Бидний хоёр хүснэгт (хамааралтай багануудаар):
loans members
id | member_id | loaned_on id | name
---+-----------+----------- ---+---------
1 | 1 | 2026-09-03 1 | Бат
2 | 1 | 2026-09-10 2 | Сараа
3 | 2 | 2026-09-12 3 | Тэмүүлэн
4 | 1 | 2026-09-20 4 | Номин
5 | 4 | 2026-09-25 5 | Ану
6 | 2 | 2026-10-01
7 | 3 | 2026-10-03
8 | 4 | 2026-10-05Query:
sql
SELECT loans.id, loans.loaned_on, members.name
FROM loans
INNER JOIN members ON loans.member_id = members.id
ORDER BY loans.id;Үр дүн:
id | loaned_on | name
---+------------+---------
1 | 2026-09-03 | Бат
2 | 2026-09-10 | Бат
3 | 2026-09-12 | Сараа
4 | 2026-09-20 | Бат
5 | 2026-09-25 | Номин
6 | 2026-10-01 | Сараа
7 | 2026-10-03 | Тэмүүлэн
8 | 2026-10-05 | НоминЭнэ query юу гэж хэлж байна вэ гэвэл: “loans хүснэгтийг members хүснэгттэй, зээлийн member_id нь гишүүний id-тай тэнцүү байх дүрмээр нийлүүлээд, зээлийн дугаар, зээлсэн огноо, гишүүний нэрийг зээлийн дугаараар эрэмбэлж харуул.”
Шинэ хэсгүүдийг задалъя. FROM loans — эхний дэвтэр, Бүлэг 1-ээс хойш танил. INNER JOIN members — хоёр дахь дэвтрийг зэрэгцүүл. ON loans.member_id = members.id — тааруулах дүрэм: зөвхөн энэ нөхцөл үнэн байх хосуудыг үлдээ. ON-ийн араас ирэх нөхцөл нь WHERE-ийн нөхцөлтэй яг адилхан хэлбэртэй — өмнөх хичээлд JOIN-ийг "хосуудыг шүүх" гэж ойлгосон нь энд бичлэг болж илэрлээ. SELECT дахь loans.id, members.name гэх мэт цэгтэй нэрсийг бүрэн нэр (qualified name) гэдэг: цэгийн өмнө хүснэгт, ард нь багана. Энэ нь номын санчийн "аль дэвтрийн дугаар" гэсэн асуултад хариулж байна.
Одоо мөр бүрийн хувь заяаг мөшгье. Өгөгдлийн сан зээл 1-ийг авч, түүний member_id (1)-тэй тэнцүү id-тай гишүүнийг хайна — Бат. Нөхцөл үнэн, хос үр дүнд оров. Зээл 2: member_id 1 — дахин Бат. Зээл 3: 2 — Сараа. Зээл 4: 1 — Бат. Зээл 5: 4 — Номин. Зээл 6: 2 — Сараа. Зээл 7: 3 — Тэмүүлэн. Зээл 8: 4 — Номин. Найман зээл бүр яг нэг гишүүнтэй тааралдсан тул үр дүн найман мөр. Ану (5) ямар ч зээлтэй тааралдаагүй тул INNER JOIN түүнийг хасав — өмнөх хичээлийн Жишээ 3-ын "тааралгүй мөр алга болох" зан. Мессежийн талбарын 8 rows returned нь бидний таамаглалтай таарч байна.
INNER гэдэг үгийг орхиж болно: JOIN members ON ... нь INNER JOIN-тэй яг ижил. Энэ хичээлд бид INNER-ийг ил тод бичиж байгаа ч бодит кодонд зүгээр JOIN гэж бичих нь түгээмэл. Дараагийн хичээлийн LEFT JOIN-тэй андуурахгүйн тулд эхэндээ INNER-ийг бичих нь сайн дадал.
Жишээ 1: ижил нэртэй багана
Query-г товчлох гээд баганын бүрэн нэрийг орхиё:
sql
SELECT id, loaned_on, name
FROM loans
INNER JOIN members ON member_id = members.id;Үр дүн:
ambiguous column name: idЭнэ мессеж юу гэж хэлж байна вэ гэвэл: “id гэдэг баганын нэр хоёрдмол утгатай байна.” loans ч, members ч id баганатай — өгөгдлийн сан аль нэгийг нь сонгох эрхгүй тул зогссон. Энэ бол номын санчийн "аль дэвтрийн дугаар" гэсэн асуулт өгөгдлийн сангийн хэлээр. Харин loaned_on, name, member_id зөвхөн нэг хүснэгтэд байгаа тул тэдгээрт асуудал гараагүй — SQLite тэднийг хаанаас хайхаа өөрөө мэдэж байна.
Засах арга нь хоёрдмол баганыг бүрэн нэрээр бичих: loans.id. Гэвч энэ хүснэгтүүдэд одоогоор давхцаагүй багана ирээдүйд давхцаж болно — жишээ нь хэн нэгэн members-д loaned_on гэсэн багана нэмбэл энэ query эвдэрнэ. Тиймээс JOIN-тэй query-д бүх баганыг бүрэн нэрээр бичих нь найдвартай дадал юм. Тэр нь уншиж буй хүнд ч мэдээлэл өгдөг: members.name гэж харвал нэр нь гишүүнийх гэдгийг шууд мэднэ.
Жишээ 2: alias — хүснэгтийн товч нэр
Бүрэн нэрсийг бичих нь query-г урт болгодог. SQL-д хүснэгтэд түр зуурын товч нэр (alias) өгч болно. Анхны жишээг дахин бичье:
sql
SELECT l.id, l.loaned_on, m.name AS member_name
FROM loans AS l
INNER JOIN members AS m ON l.member_id = m.id
ORDER BY l.id;Үр дүн:
id | loaned_on | member_name
---+------------+------------
1 | 2026-09-03 | Бат
2 | 2026-09-10 | Бат
3 | 2026-09-12 | Сараа
4 | 2026-09-20 | Бат
5 | 2026-09-25 | Номин
6 | 2026-10-01 | Сараа
7 | 2026-10-03 | Тэмүүлэн
8 | 2026-10-05 | НоминЭнэ query юу гэж хэлж байна вэ гэвэл: “loans-ийг l гэж, members-ийг m гэж нэрлээд, l-ийн member_id нь m-ийн id-тай тэнцүү хосуудын зээлийн дугаар, огноо, гишүүний нэрийг (member_name гэж нэрлэн) зээлийн дугаараар эрэмбэлж харуул.” Мөрүүд Анхны жишээтэй яг ижил — зөвхөн бичлэг товчилсон. FROM loans AS l нь "энэ query-ний турш loans-ийг l гэж дуудна" гэсэн утгатай. Alias нь зөвхөн тухайн query-ний дотор амьдардаг бөгөөд хүснэгтийн жинхэнэ нэрийг өөрчлөхгүй.
Энэ query-д хоёр төрлийн alias байгааг анзаараарай. Хүснэгтийн alias (loans AS l) нь query-г товчилдог. Баганын alias (m.name AS member_name) нь үр дүнгийн толгойн нэрийг өөрчилдөг — Бүлэг 1-ийн SELECT-ийн анатоми хичээлд таслал мартахад санамсаргүй гарч ирсэн тэр боломжийг энд санаатайгаар ашиглаж байна. JOIN-ийн үр дүнд хоёр хүснэгт хоёулаа name баганатай байх үед (жишээ нь гишүүн ба зохиогчийн нэр) баганын alias тэднийг ялгах цорын ганц арга болно. AS үгийг орхиж болно (loans l), гэхдээ Бүлэг 1-ийн таслалын алдааг санаж, энэ сургалтад AS-ийг үргэлж бичнэ. Alias-ийг утгатай үсгээр сонгох нь сайн: l — loans, m — members, b — books.
Жишээ 3: JOIN ба WHERE хамт
Одоо Түвшин 2-ын удирдамжтай дасгалын Алхам 7-ын асуултад буцаж очъё — одоо гарт байгаа номнууд, гэхдээ дугаар биш нэрээр. Энэ удаад зээлийг номтой нийлүүлнэ:
books (хамааралтай мөрүүд)
id | title
---+-------------------------
2 | Тунгалаг Тамир
4 | Clean Code
6 | The Pragmatic ProgrammerQuery:
sql
SELECT b.title, l.loaned_on, l.due_on
FROM loans AS l
INNER JOIN books AS b ON l.book_id = b.id
WHERE l.returned_on IS NULL
ORDER BY l.due_on;Үр дүн:
title | loaned_on | due_on
-------------------------+------------+-----------
Тунгалаг Тамир | 2026-09-20 | 2026-10-04
Clean Code | 2026-10-01 | 2026-10-15
The Pragmatic Programmer | 2026-10-03 | 2026-10-17Энэ query юу гэж хэлж байна вэ гэвэл: “loans-ийг books-той зээлийн book_id нь номын id-тай тэнцүү байх дүрмээр нийлүүлж, буцаагаагүй зээлүүдийн номын нэр, зээлсэн болон буцаах ёстой огноог буцаах огноогоор эрэмбэлж харуул.”
JOIN нь Бүлэг 1-ийн SELECT-ийн араг ясны FROM нүдний өргөтгөл юм: FROM-ийн араас JOIN ... ON ... ирж, дараа нь WHERE, ORDER BY өмнөх шигээ байрандаа. Гүйцэтгэх дарааллын хувьд өгөгдлийн сан эхлээд хүснэгтүүдийг нийлүүлж (FROM ба JOIN), дараа нь нийлсэн мөрүүдийг WHERE-ээр шүүж, SELECT-ээр баганыг сонгож, ORDER BY-ээр эрэмбэлнэ. Тиймээс WHERE-д аль ч хүснэгтийн баганыг ашиглаж болно. Бид l.returned_on-оор шүүсэн боловч b.genre = 'Програмчлал' гэж номын баганаар ч шүүж болох байсан. Найман зээлээс JOIN найман хос үүсгэж, WHERE тэдгээрээс буцаагаагүй гурвыг үлдээсэн.
Жишээ 4: дарааллыг сольж бичих
INNER JOIN-д аль хүснэгтийг FROM-д, аль нь JOIN-д бичих нь үр дүнгийн мөрүүдийг өөрчилдөггүй. Query:
sql
SELECT m.name, l.loaned_on
FROM members AS m
INNER JOIN loans AS l ON l.member_id = m.id
ORDER BY m.name, l.loaned_on;Үр дүн:
name | loaned_on
---------+-----------
Бат | 2026-09-03
Бат | 2026-09-10
Бат | 2026-09-20
Номин | 2026-09-25
Номин | 2026-10-05
Сараа | 2026-09-12
Сараа | 2026-10-01
Тэмүүлэн | 2026-10-03Энэ query юу гэж хэлж байна вэ гэвэл: “members-ийг loans-тай гишүүний id нь зээлийн member_id-тай тэнцүү байх дүрмээр нийлүүлээд, гишүүний нэр ба зээлсэн огноог нэр, огноогоор эрэмбэлж харуул.” Энэ удаад гишүүдээс эхэлсэн ч найман хос яг өмнөх шигээ гарч ирсэн, Ану мөн адил алга. INNER JOIN тэгш хэмтэй: "зээлийг гишүүнтэй тааруул" ба "гишүүнийг зээлтэй тааруул" нь ижил хосууд. ON доторх тэгш байдлын хоёр талыг ч сольж болно. Харин үр дүнгийн дараалал ORDER BY-гүйгээр баталгаагүй тул бид үргэлж ORDER BY бичнэ. Бодит ажилд "энэ query юуны тухай вэ" гэдэг утгаар нь FROM-ийг сонгох нь уншихад тустай: "гишүүд ба тэдний зээлүүд" бол members-ээс, "зээлүүд ба тэдгээрийн гишүүд" бол loans-оос эхэл.
Энэ тэгш хэм дараагийн хичээлийн LEFT JOIN-д эвдэрнэ — тэнд дараалал маш чухал болно.
Жишээ 5: ON-ийг мартах — тэсрэлт
Өмнөх хичээлийн "бүх боломжит хос" загвар бодит болох мөч. ON хэсгийг мартъя:
sql
SELECT l.id, m.name
FROM loans AS l
INNER JOIN members AS m;Мессеж: 40 rows returned. Үр дүнгийн эхний мөрүүд:
id | name
---+---------
1 | Бат
1 | Сараа
1 | Тэмүүлэн
1 | Номин
1 | Ану
2 | Бат
2 | Сараа
...Энэ query юу гэж хэлж байна вэ гэвэл: “loans-ийг members-тэй нийлүүл” — ямар дүрмээр гэдгийг хэлээгүй. SQLite үүнийг алдаа гэж үзэлгүй, зээл бүрийг гишүүн бүртэй хослуулсан: 8 × 5 = 40 мөр. Зээл 1 таван өөр гишүүнтэй "холбогдсон" — үүний дөрөв нь худал. Ийм бүх хосын нийлбэрийг cross join эсвэл декартын үржвэр гэдэг бөгөөд энэ нь өмнөх хичээлийн 15 хосын хүснэгтийн бүрэн хувилбар юм. Манай жижиг хүснэгтэд 40 мөр нь гайхмаар л санагдана. Мянган зээл, мянган гишүүнтэй хүснэгтэд нэг сая мөр болж, query удаан ажиллаж, хэрэглэгчийн дэлгэц гацаж болно.
Зарим өгөгдлийн сан (жишээ нь PostgreSQL) ON-гүй INNER JOIN-ийг синтаксийн алдаа гэж үздэг бол SQLite чимээгүй хүлээн авдаг. Тиймээс SQLite-д ажиллахдаа мессежийн мөрийн тоог хүлээлттэй харьцуулах нь цорын ганц хамгаалалт: найман зээлтэй нийлүүлэхэд 8 орчим биш, 40 гарвал ON-оо шалгаарай.
Жишээ 6: буруу баганыг тааруулах — алдаагүй худал
Одоо хамгийн нууцлаг алдаа. ON-д санамсаргүй book_id бичье:
sql
SELECT l.id, m.name
FROM loans AS l
INNER JOIN members AS m ON l.book_id = m.id
ORDER BY l.id;Үр дүн:
id | name
---+------
1 | Номин
2 | Ану
3 | Сараа
4 | Сараа
5 | Номин
6 | НоминЭнэ query юу гэж хэлж байна вэ гэвэл: "зээлийн номын дугаарыг гишүүний дугаартай тааруул" — утгагүй дүрэм. Гэвч SQLite үүнийг мэдэхгүй: тоонууд тоонуудтай тэнцүү эсэхийг л шалгадаг. Зээл 1-ийн book_id нь 4 — гишүүн №4 Номин. Үр дүн "Номин зээл 1-ийг авсан" гэж хэлж байна, гэтэл Бат авсан. Ану ганц ч ном зээлээгүй атлаа зээл 2-ийн эзэн болжээ. Зээл 7 (book_id 6) болон 8 (book_id 8) гишүүнтэй тааралдаагүй тул алга болсон. Зургаан мөр, зургаан худал, алдааны мессеж байхгүй.
Энэ нь өмнөх хичээлийн Жишээ 4-ийн бодит хувилбар бөгөөд түүнээс хамгаалах цорын ганц арга нь ON-ийг бичихдээ заалтыг түүний заадаг түлхүүртэй хослуулах дадал юм: member_id нь members.id-тай, book_id нь books.id-тай. Заалтын баганын нэр (member_id) болон тааруулж буй хүснэгт (m, members) хоорондоо "таарч" байгаа эсэхийг нүдээр шалгаарай. Мөн үр дүнгийн хэдэн мөрийг анхны хүснэгтүүдтэй тулгаж шалгах — "зээл 1-ийг үнэхээр Бат авсан уу" — нь Бүлэг 1-ийн "алдаагүй ажилласан ч үр дүнг нүдээр шалга" дадлын JOIN хувилбар юм.
Бусад хүний бичсэн SQL-д та JOIN үггүй ийм хэлбэртэй тааралдаж магадгүй:
sql
SELECT l.id, m.name
FROM loans AS l, members AS m
WHERE l.member_id = m.id;Энэ нь INNER JOIN-тэй ижил үр дүн өгдөг хуучин хэлбэр юм: FROM-д хүснэгтүүдийг таслалаар жагсаах нь "бүх хос" үүсгэж, WHERE тэдгээрийг шүүнэ — өмнөх хичээлийн загварын шууд бичлэг. Таних нь хэрэгтэй, гэвч бичихдээ JOIN ... ON хэлбэрийг сонгоорой: тааруулах дүрэм (ON) болон шүүлтийн нөхцөл (WHERE) тусдаа байх тул query тодорхой болж, ON-ийг мартах нь илүү харагдахуйц болно.
Хэрэв сонирхож байвал book_authors болон authors хүснэгтүүдийг INNER JOIN-оор нийлүүлж, зохиогч бүрийн нэрийг book_id-тай хамт харуулж үзээрэй. Өмнөх хичээлийн "Туршаад үзээрэй" хэсэгт цаасан дээр таамагласан үр дүнтэйгээ харьцуулаарай: хэдэн мөр гарсан бэ? Al Sweigart гарсан уу? Мөн Жишээ 3-ын query-г номын жанраар шүүж (WHERE b.genre = 'Програмчлал'), буцаагдсан эсэхээс үл хамааран програмчлалын номын бүх зээлийг харж болно. Ажиллуулахаасаа өмнө мөрийн тоог таамаглах дадлаа мартуузай. Эдгээр нь зөвхөн санал бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж тооцохгүй.
Хоёрдмол баганын нэр. Жишээ 1-д харсан ambiguous column name: id нь JOIN-ий хамгийн түгээмэл алдаа. Мессеж нь асуудалтай баганыг яг нэрлэдэг тул засахад хялбар: тэр баганын өмнө хүснэгтийн нэр эсвэл alias бич. Урьдчилан сэргийлэх арга нь JOIN-тэй query-д бүх баганыг бүрэн нэрээр бичих.
Alias өгсний дараа жинхэнэ нэрийг ашиглах. FROM loans AS l гэж бичээд дараа нь loans.member_id гэвэл no such column: loans.member_id гарна. Энэ мессеж юу гэж хэлж байна вэ гэвэл: “loans.member_id гэдэг багана байхгүй байна.” Хүснэгтэд alias өгсний дараа тэр query-ний турш зөвхөн alias-аар нь дуудна — жинхэнэ нэр нь query-ний дотор "далд" болно. Нэг query-д жинхэнэ нэр ба alias-ийг холихгүй байх нь энгийн дүрэм.
ON-ийг мартах. Жишээ 5-ын дагуу SQLite алдаа өгөхгүй, мөрүүд үржинэ. “rows returned” тоо хүснэгтүүдийн мөрийн тооны үржвэртэй тэнцүү (энд 40 = 8 × 5) гарвал энэ нь ON-гүй JOIN-ийн дохио.
Буруу баганыг тааруулах. Жишээ 6-ийн алдаа нэг ч дохио өгдөггүй. ON-ийг бичихдээ "заалт ба түүний заадаг түлхүүр" хосыг шалга: l.member_id = m.id — member_id ба members; l.book_id = b.id — book_id ба books. Хослол таарахгүй харагдвал тэр нь алдаа.
ON-ийн нөхцөлд WHERE-ийн шүүлтийг хольж бичих. INNER JOIN-д ON l.book_id = b.id AND l.returned_on IS NULL гэж бичвэл Жишээ 3-тай ижил үр дүн гарна — INNER JOIN-д ON ба WHERE хоёулаа зүгээр л хос бүрийг шүүдэг. Гэвч энэ нь query-г уншихад төөрөгдүүлдэг бөгөөд дараагийн хичээлийн LEFT JOIN-д огт өөр утгатай болно. Дүрэм: ON-д зөвхөн тааруулах дүрэм, WHERE-д шүүлтийн нөхцөл.
Энэ хичээлд та INNER JOIN-ийн бичлэгийг сурлаа: FROM эхний_хүснэгт INNER JOIN хоёр_дахь_хүснэгт ON тааруулах_дүрэм. ON-ийн нөхцөл нь FOREIGN KEY-ийг түүний заадаг PRIMARY KEY-тэй холбож, зөвхөн тааралдсан хосуудыг үлдээдэг; INNER гэдэг үгийг орхиж болно. Хоёр хүснэгтэд ижил нэртэй баганыг хүснэгт.багана бүрэн нэрээр ялгах ба alias (loans AS l) нь query-г товч, уншигдахуйц болгодог; баганын alias (AS member_name) нь үр дүнгийн толгойг нэрлэдэг. JOIN нь FROM-ийн өргөтгөл бөгөөд WHERE, ORDER BY байрандаа хэвээр, хоёр хүснэгтийн баганыг хоёуланг нь ашиглаж чадна. INNER JOIN тэгш хэмтэй — хүснэгтийн дарааллыг сольсон ч ижил хосууд гарна. ON-гүй JOIN мөрүүдийг үржүүлж, буруу ON алдаагүй худал үр дүн өгдөг тул мөрийн тоог таамаглаж, хэдэн мөрийг тулгаж шалгах дадал хэрэгтэй.
Дараагийн хичээлд INNER JOIN-ий хассан мөрүүд рүү эргэж очно: хэзээ ч ном зээлээгүй Ану, хэзээ ч зээлэгдээгүй номнууд. LEFT JOIN нь нэг талын бүх мөрийг хадгалж, тааралгүй мөрүүдийг NULL-ээр дүүргэдэг. Бид INNER ба LEFT JOIN-ийг ижил өгөгдөл дээр зэрэгцүүлэн харьцуулж, "хэзээ ч зээлээгүй гишүүдийг ол" гэсэн сонгодог асуултыг шийднэ.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.