Ачааллж байна...
Энэ бүлгийн өмнөх гурван хичээлд бид WHERE-ээр ямар мөрүүд үр дүнд орохыг шийдэж сурсан. Гэвч үр дүнд орох мөрүүдийг сонгох нь ажлын тал нь л юм. Нөгөө тал нь тэр мөрүүдийг хүнд уншигдахуйц хэлбэрт оруулах: шинэ номнууд эхэндээ байх уу, хуучин нь уу; хамгийн их хувьтай гурван номыг л харах уу, бүгдийг нь харах уу; нэг хот гурван удаа давтагдах ёстой юу, нэг удаа л харагдах уу.
Энэ хичээлд эдгээр гурван асуултад хариулах гурван хэрэгслийг сурна. ORDER BY нь мөрүүдийг нэг эсвэл хэд хэдэн баганаар эрэмбэлдэг. LIMIT нь эхний хэдэн мөрийг л авдаг бөгөөд ORDER BY-тэй хослоод "хамгийн их", "хамгийн шинэ" гэх мэт асуултад хариулдаг. DISTINCT нь давтагдсан мөрүүдийг нэгтгэдэг. Гурвуулаа өгөгдлийг өөрчилдөггүй, зөвхөн үр дүнгийн хэлбэрийг өөрчилдөг.
Мөн хоёр шударга анхааруулга энэ хичээлд бий. Нэгдүгээрт, SQLite кирилл текстийг монгол цагаан толгойн дарааллаар бус, компьютерийн дотоод кодын дарааллаар эрэмбэлдэг. Хоёрдугаарт, DISTINCT нь нэг баганыг биш, харин бүтэн мөрийг давхардалгүй болгодог. Эдгээрийг жишээгээр нь харуулна.
Номын санч шинэ ирсэн номнуудын жагсаалтыг гартаа барьж байна гэж төсөөлөөд үзээрэй. ORDER BY бол түүнд өгөх "оноор нь өр" эсвэл "нэрээр нь цагаан толгойн дарааллаар өр" гэсэн заавар юм. Номнууд өөрсдөө өөрчлөгдөхгүй, зөвхөн тавиур дээрх байрлал нь өөрчлөгдөнө. LIMIT бол "өрсөн тавиураас эхний гурвыг нь авчирч өг" гэсэн нэмэлт заавар — хэрэв эхлээд өрөөгүй бол "эхний гурав" гэдэг нь санамсаргүй гурван ном болно гэдгийг анхаараарай.
DISTINCT бол бүртгэлээс "манай гишүүд ямар ямар хотоос ирдэг вэ" гэсэн асуултын хариуг бичих арга юм. Номын санч гишүүн бүрийн хотыг нэг бүрчлэн бичихгүй, харин хот бүрийг жагсаалтад нэг удаа л бичнэ. "Улаанбаатар" гурван гишүүнтэй байсан ч жагсаалтад нэг мөр л эзэлнэ.
Бидний books хүснэгт:
id | title | author | year | genre | copies
---+--------------------------+------------------+------+-------------+-------
1 | Монголын нууц товчоо | NULL | 1240 | Түүх | 2
2 | Тунгалаг Тамир | Ч.Лодойдамба | 1962 | Роман | 3
3 | Учиртай гурван толгой | Д.Нацагдорж | 1934 | Жүжиг | 1
4 | Clean Code | Robert C. Martin | 2008 | Програмчлал | 4
5 | Python Crash Course | Eric Matthes | 2015 | Програмчлал | 5
6 | The Pragmatic Programmer | Andrew Hunt | 1999 | Програмчлал | 2Query:
sql
SELECT title, year
FROM books
ORDER BY year;Үр дүн:
title | year
-------------------------+-----
Монголын нууц товчоо | 1240
Учиртай гурван толгой | 1934
Тунгалаг Тамир | 1962
The Pragmatic Programmer | 1999
Clean Code | 2008
Python Crash Course | 2015Энэ query юу гэж хэлж байна вэ гэвэл: “books хүснэгтийн бүх мөрийг year-ээр нь бага онноос их он руу эрэмбэлээд, title болон year-ийг харуул.” Мөрүүд одоо хэвлэгдсэн оныхоо дарааллаар байрлаж, 1240 оны "Монголын нууц товчоо" эхэнд, 2015 оны “Python Crash Course” төгсгөлд гарсан. Өмнөх хичээлүүдэд үр дүн маань үргэлж id-ийн дарааллаар гарч байсан нь давхцал байсан — бид дараалал хүсээгүй тул SQLite хүснэгтэд хадгалагдсан дарааллаар нь өгсөн. ORDER BY нь энэ дарааллыг та өөрөө шийдэх боломж олгодог.
Анхдагч байдлаар ORDER BY бага утгаас их рүү (өсөх дарааллаар) эрэмбэлдэг. Үүнийг ASC (ascending — өсөх) гэж нэрлэдэг бөгөөд бичихгүй орхиж болно: ORDER BY year болон ORDER BY year ASC хоёр яг ижил. Эсрэг дарааллыг хүсвэл DESC (descending — буурах) гэж бичнэ:
sql
SELECT title, year
FROM books
ORDER BY year DESC;Үр дүн:
title | year
-------------------------+-----
Python Crash Course | 2015
Clean Code | 2008
The Pragmatic Programmer | 1999
Тунгалаг Тамир | 1962
Учиртай гурван толгой | 1934
Монголын нууц товчоо | 1240Энэ query юу гэж хэлж байна вэ гэвэл: “books хүснэгтийн мөрүүдийг year-ээр нь их онноос бага он руу эрэмбэлээд харуул.” Одоо хамгийн шинэ ном эхэнд байна. Ихэнх вэб сайт, програм "хамгийн шинэ нь эхэндээ" гэсэн дарааллыг ашигладаг тул DESC нь бодит ажилд маш олон удаа бичигддэг үг юм.
Жишээ 1: текстээр эрэмбэлэх ба кирилл үсгийн үнэн
Номнуудыг нэрээр нь эрэмбэлье. Query:
sql
SELECT title
FROM books
ORDER BY title;Үр дүн:
title
------------------------
Clean Code
Python Crash Course
The Pragmatic Programmer
Монголын нууц товчоо
Тунгалаг Тамир
Учиртай гурван толгойЭнэ query юу гэж хэлж байна вэ гэвэл: “books хүснэгтийн мөрүүдийг title-ээр нь цагаан толгойн дарааллаар эрэмбэлээд, title-ийг харуул.” Латин үсгээр бичигдсэн гурван ном эхэнд, кирилл үсгээр бичигдсэн гурван ном араас нь гарсныг анзаараарай. Бүлэг бүрийн дотор дараалал зөв байна: C, P, T, мөн М, Т, У. Гэвч латин бүх үсэг кирилл бүх үсгээс түрүүлж байгаа нь ямар нэг цагаан толгойн дүрэм биш, харин компьютер үсэг бүрийг дотроо тоогоор хадгалдагтай холбоотой. SQLite анхдагч байдлаар текстийг тэр тоонуудаар нь харьцуулдаг бөгөөд латин үсгүүдийн тоо кирилл үсгүүдийнхээс бага байдаг.
Энэ нь монгол текстэд нэг шударга анхааруулга авчирдаг. Кирилл цагаан толгойн ихэнх үсэг (А, Б, В ... Я) зөв дарааллаар эрэмбэлэгддэг. Гэвч монгол хэлний Ө болон Ү үсэг компьютерийн кодын хүснэгтэд үндсэн кирилл үсгүүдээс тусдаа, хол байрладаг тул SQLite тэдгээрийг Я-гийн дараа тавьдаг. Өөрөөр хэлбэл "Өглөө" гэсэн үгтэй нэр монгол цагаан толгойн дарааллаар О-гийн дараа байх ёстой атлаа жагсаалтын хамгийн төгсгөлд гарна. Манай хүснэгтэд Ө, Ү-ээр эхэлсэн нэр байхгүй тул энэ асуудал одоо харагдахгүй байгаа ч өөрийн өгөгдөл дээр гарч ирвэл гайхах хэрэггүй. PostgreSQL-д хэлний дүрэмд нийцсэн эрэмбэлэлт тохируулах арга бий бөгөөд үүнийг энэ сургалтын хүрээнд нарийвчлан судлахгүй ч ийм боломж байгааг мэдэж байх нь зүйтэй.
Жишээ 2: хэд хэдэн баганаар эрэмбэлэх
Одоо members хүснэгт рүү шилжье:
id | name | city | joined_year | email
---+----------+-------------+-------------+------------------
1 | Бат | Улаанбаатар | 2023 | bat@example.com
2 | Сараа | Дархан | 2024 | saraa@example.com
3 | Тэмүүлэн | Улаанбаатар | 2022 | NULL
4 | Номин | Эрдэнэт | 2024 | nomin@example.com
5 | Ану | Улаанбаатар | 2025 | anu@example.comГишүүдийг хотоор нь бүлэглэн харуулж, хот бүрийн дотор хамгийн сүүлд элссэнийг нь эхэнд тавья. Query:
sql
SELECT name, city, joined_year
FROM members
ORDER BY city, joined_year DESC;Үр дүн:
name | city | joined_year
---------+-------------+------------
Сараа | Дархан | 2024
Ану | Улаанбаатар | 2025
Бат | Улаанбаатар | 2023
Тэмүүлэн | Улаанбаатар | 2022
Номин | Эрдэнэт | 2024Энэ query юу гэж хэлж байна вэ гэвэл: “members хүснэгтийн мөрүүдийг эхлээд city-ээр нь цагаан толгойн дарааллаар, хот нь ижил бол joined_year-ээр нь их онноос бага он руу эрэмбэлээд харуул.” Олон баганаар эрэмбэлэх нь утасны дэвтэр шиг ажилладаг: эхлээд овгоор, овог ижил бол нэрээр. Эхний багана (city) гол дарааллыг тогтоосон: Дархан, Улаанбаатар, Эрдэнэт. Хоёр дахь багана (joined_year) зөвхөн эхний баганын утга ижил мөрүүдийн хооронд л үүрэг гүйцэтгэсэн — Улаанбаатарын гурван гишүүнийг 2025, 2023, 2022 гэж буурах дарааллаар байрлуулсан. Дархан, Эрдэнэт тус бүр нэг л гишүүнтэй тул хоёр дахь баганад эрэмбэлэх зүйл байгаагүй.
Энд маш чухал нэг нарийн зүйл бий: DESC нь зөвхөн өөрийнхөө яг өмнөх баганад хамаардаг. ORDER BY city, joined_year DESC нь "хот болон оныг хоёуланг нь буурах дарааллаар" гэсэн утга биш — city нь анхдагч өсөх дарааллаараа үлдсэн. Хэрэв хоёуланг нь буурах дарааллаар хүсвэл багана бүрийн араас DESC бичих хэрэгтэй: ORDER BY city DESC, joined_year DESC.
Жишээ 3: LIMIT ба хамгийн их N
Номын санд хамгийн олон хувьтай гурван номыг олъё. Query:
sql
SELECT title, copies
FROM books
ORDER BY copies DESC
LIMIT 3;Үр дүн:
title | copies
--------------------+-------
Python Crash Course | 5
Clean Code | 4
Тунгалаг Тамир | 3Энэ query юу гэж хэлж байна вэ гэвэл: “books хүснэгтийн мөрүүдийг copies-оор нь ихээс бага руу эрэмбэлээд, эхний гурвынх нь title болон copies-ыг харуул.” Энэ хослол — ORDER BY ... DESC, дараа нь LIMIT — нь "хамгийн их N", "хамгийн шинэ N", "хамгийн өндөр үнэлгээтэй N" гэсэн бүх асуултын стандарт хэлбэр бөгөөд top-N query гэж нэрлэдэг. Бүлэг 1-д гүйцэтгэх дарааллыг харсныг санаарай: эхлээд ORDER BY бүх зургаан мөрийг эрэмбэлж, дараа нь LIMIT эхний гурвыг таслан авдаг. Тийм учраас үр дүн нь үнэхээр хамгийн их гурав юм.
Одоо шударга нэг асуудлыг харъя. Хэрэв LIMIT 4 гэж бичвэл дөрөв дэх байранд хэн гарах вэ? Хоёр хувьтай ном хоёр бий: "Монголын нууц товчоо" болон “The Pragmatic Programmer”. Тэд copies-оор тэнцэж байгаа тул ORDER BY тэдгээрийн хооронд дараалал тогтоох мэдээлэлгүй, LIMIT 4 нь аль нэгийг нь л авна. Аль нь гарахыг SQL баталдаггүй — өнөөдөр нэг нь, өгөгдөл өөрчлөгдсөний дараа нөгөө нь гарч болно. Энэ асуудлыг шийдэх арга нь тэнцээ шийдэх нэмэлт багана өгөх: ORDER BY copies DESC, title. Тэгвэл хувийн тоо тэнцсэн үед нэрийн дарааллаар шийдэгдэж, үр дүн үргэлж ижил байна.
LIMIT-ийн тухай хоёр нэмэлт зүйл. Нэгдүгээрт, ORDER BY-гүй LIMIT нь "дурын N мөр" гэсэн утгатай. SELECT title FROM books LIMIT 3; нь одоогоор эхний гурван id-тай номыг буцааж магадгүй ч энэ нь баталгаагүй. "Ямар нэг жишээ мөр харах" зорилгоор л ингэж бичдэг. Хоёрдугаарт, LIMIT-тэй OFFSET гэдэг хамтрагч бий: LIMIT 2 OFFSET 2 нь "эхний хоёрыг алгасаад, дараагийн хоёрыг ав" гэсэн утгатай. Вэб сайтын “2-р хуудас”, “3-р хуудас” гэх мэт хуудаслалт (pagination) яг ингэж ажилладаг — хуудас бүр нэг LIMIT, өөр өөр OFFSET.
Жишээ 4: DISTINCT — давхардалгүй утгууд
Номын сангийн гишүүд ямар хотуудаас ирдгийг харъя. Энгийнээр SELECT city FROM members; гэвэл таван мөр ирж, "Улаанбаатар" гурван удаа давтагдана. DISTINCT нэмье. Query:
sql
SELECT DISTINCT city
FROM members
ORDER BY city;Үр дүн:
city
-----------
Дархан
Улаанбаатар
ЭрдэнэтЭнэ query юу гэж хэлж байна вэ гэвэл: “members хүснэгтээс city-ийн давхардалгүй утгуудыг цагаан толгойн дарааллаар харуул.” DISTINCT нь SELECT-ийн яг араас бичигддэг бөгөөд үр дүнд ижил мөр хоёр удаа гарахыг хориглодог. Таван гишүүний таван хот байснаас гурван ялгаатай утга л үлдсэн. Бид ORDER BY нэмсэн нь санаатай: DISTINCT нь өөрөө дарааллыг баталдаггүй тул эрэмбэлсэн жагсаалт хүсвэл ORDER BY-г тусад нь бичих хэрэгтэй.
Жишээ 5: DISTINCT бүтэн мөрөнд хамаардаг
Одоо DISTINCT-ийн тухай хамгийн чухал үнэнийг харъя. Хот болон элссэн оныг хамт сонгоод DISTINCT хэрэглэе. Query:
sql
SELECT DISTINCT city, joined_year
FROM members;Үр дүн:
city | joined_year
------------+------------
Улаанбаатар | 2023
Дархан | 2024
Улаанбаатар | 2022
Эрдэнэт | 2024
Улаанбаатар | 2025Энэ query юу гэж хэлж байна вэ гэвэл: “members хүснэгтээс city болон joined_year-ийн давхардалгүй хослолуудыг харуул.” Таван мөр бүгд ирсэн бөгөөд "Улаанбаатар" дахин гурван удаа гарч ирлээ. Учир нь DISTINCT нь "энэ багана давхардахгүй" гэдгийг биш, харин "бүтэн мөр давхардахгүй" гэдгийг баталдаг. ("Улаанбаатар", 2023), ("Улаанбаатар", 2022), ("Улаанбаатар", 2025) гэсэн гурван мөр хот нь ижил ч он нь өөр тул гурвуулаа ялгаатай мөрүүд юм. Үүний нэгэн адил ("Дархан", 2024) болон ("Эрдэнэт", 2024) он нь ижил ч хот нь өөр тул хоёулаа үлдсэн.
Олон хүн SELECT DISTINCT city, joined_year гэж бичихдээ "хот давхардахгүй, харин он нь хамт гарна" гэж хүлээдэг. Гэвч хэрэв Улаанбаатар нэг л мөрөнд гарах ёстой бол түүний хажууд 2023, 2022, 2025-ийн алийг нь бичих вэ? Ийм асуултад хариулахын тулд мөрүүдийг бүлэглэж, бүлэг бүрт нэг утга тооцоолох хэрэгтэй — жишээ нь "хот бүрт хамгийн сүүлийн элссэн он". Энэ бол GROUP BY-ийн ажил бөгөөд Бүлэг 8-д бүхэлд нь судална. Одоохондоо нэг дүрэм санаарай: DISTINCT нь SELECT-д бичсэн бүх баганын хослолыг давхардалгүй болгодог.
Мөн өмнөх хичээлтэй холбоотой нэг жижиг тэмдэглэл: DISTINCT нь NULL-уудыг хооронд нь ижил гэж үздэг. Хэрэв хоёр гишүүний имэйл хоёулаа NULL байсан бол SELECT DISTINCT email нь нэг л NULL мөр буцаана. Энэ нь NULL = NULL "мэдэхгүй" байдагтай зөрчилдөж байгаа мэт санагдаж болох ч SQL-ийн стандарт давхардал арилгах үед NULL-уудыг нэг бүлэгт хамааруулахаар тогтоосон байдаг.
Өмнөх хичээлийн "мэдэхгүй" утга эрэмбэлэлтэд хаана байрлах вэ? Хэрэв бид books-ийг author-оор эрэмбэлбэл "Монголын нууц товчоо" (author нь NULL) SQLite дээр жагсаалтын хамгийн эхэнд гарна, учир нь SQLite нь NULL-ийг бүх утгаас бага гэж үздэг. DESC ашиглавал хамгийн төгсгөлд шилжинэ. Анхааруулга: PostgreSQL эсрэгээр нь, NULL-ийг бүх утгаас их гэж үзэж өсөх дарааллын төгсгөлд тавьдаг. Хэрэв NULL-ийн байрлал танд чухал бол хоёр өгөгдлийн сан хоёулаа дэмждэг ORDER BY author NULLS LAST (эсвэл NULLS FIRST) хэлбэрээр ил тод хэлж болно. Ингэснээр нэг query хоёр өөр өгөгдлийн сан дээр ижил үр дүн өгнө.
Хэрэв сонирхож байвал books хүснэгтээс хамгийн эртний хоёр номыг, эсвэл програмчлалын номнуудыг хувийн тоогоор нь ихээс бага руу эрэмбэлсэн жагсаалтыг гаргаж үзээрэй (WHERE болон ORDER BY-г хослуулах — араг ясны дарааллыг санаарай). Мөн SELECT DISTINCT genre FROM books; болон SELECT DISTINCT genre, copies FROM books; хоёрыг ажиллуулж, мөрийн тоог харьцуулбал DISTINCT-ийн бүтэн мөрийн дүрмийг өөрийн өгөгдөл дээр дахин харах болно. Эдгээр нь зөвхөн санал бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж тооцохгүй.
DESC-ийг баганын өмнө бичих. ORDER BY DESC year гэж бичвэл near "DESC": syntax error гэсэн алдаа гарна. Энэ мессеж юу гэж хэлж байна вэ гэвэл: “DESC гэдэг үгийн ойролцоо SQL-ийн дүрэм зөрчигдлөө.” SQLite ORDER BY-ийн араас эрэмбэлэх баганыг хүлээж байтал чиглэлийн үг ирсэн. DESC болон ASC үргэлж баганын араас бичигдэнэ: ORDER BY year DESC.
DESC бүх баганад хамаарна гэж бодох. Жишээ 2-т тайлбарласанчлан ORDER BY city, joined_year DESC нь зөвхөн joined_year-ийг буурах дарааллаар эрэмбэлдэг. Энэ үед алдааны мессеж гарахгүй, зөвхөн дараалал таны хүлээснээс өөр байна. Багана бүрийн чиглэлийг тусад нь бичих дадал гаргаарай.
LIMIT-ийг ORDER BY-ийн өмнө бичих. SELECT title FROM books LIMIT 3 ORDER BY copies DESC; гэж бичвэл near "ORDER": syntax error гарна. Бүлэг 1-ийн араг ясанд LIMIT хамгийн сүүлд байдаг. Логикийн хувьд ч энэ зөв: эхлээд эрэмбэлж, дараа нь таслах ёстой.
DISTINCT-ийг баганын араас бичих. SELECT city DISTINCT FROM members; гэж бичвэл near "DISTINCT": syntax error гарна. DISTINCT нь SELECT-ийн яг араас, бүх баганын өмнө ирдэг бөгөөд нэг баганад биш, бүтэн мөрөнд хамаардаг гэдгийг энэ байрлал нь өөрөө илэрхийлж байна.
ORDER BY-гүй LIMIT-д найдах. LIMIT 3-ийг ORDER BY-гүй бичиж "эхний гурав" гэж тайланд ашиглавал алдааны мессеж хэзээ ч гарахгүй, гэвч өгөгдөл өөрчлөгдөх, эсвэл PostgreSQL руу шилжих үед "эхний гурав" өөр мөрүүд болж болно. "Эхний", "хамгийн" гэсэн үг агуулсан асуулт бүрт ORDER BY заавал хэрэгтэй.
Энэ хичээлд та үр дүнг хэлбэржүүлэх гурван хэрэгслийг сурлаа. ORDER BY нь мөрүүдийг эрэмбэлдэг; анхдагч дараалал нь өсөх (ASC), эсрэгийг нь DESC гэж бичнэ, хэд хэдэн баганаар эрэмбэлэхэд хойдох багана зөвхөн өмнөх баганын утга тэнцсэн үед үүрэг гүйцэтгэж, DESC зөвхөн өөрийн баганадаа хамаарна. SQLite кирилл текстийг кодын дарааллаар эрэмбэлдэг тул латин үсэг түрүүлж, Ө болон Ү төгсгөлд гардаг. LIMIT нь эхний N мөрийг авдаг бөгөөд ORDER BY-тэй хослоод top-N query болдог; тэнцээ шийдэх нэмэлт багана өгөх нь үр дүнг тогтвортой болгоно. DISTINCT нь бүтэн мөрийн давхардлыг арилгадаг.
Ингээд Бүлэг 2 дууслаа. Та одоо хүснэгтээс хүссэн мөрүүдээ шүүж, хүссэн дарааллаараа, хүссэн тоогоор нь харуулж чадна — SQL-ийн унших талыг бараг бүрэн эзэмшлээ. Дараагийн бүлэгт бид анх удаа өгөгдлийг өөрчилнө. INSERT-ээр шинэ мөр нэмж, UPDATE-ээр засаж, DELETE-ээр устгана. SELECT-ийн аюулгүй ертөнцөөс гарч байгаа тул тэнд болгоомжлолын дадал гол сэдэв болно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.