Ачааллж байна...
Түвшин 2-т тавтай морилно уу. Түвшин 1-д бид бэлэн хүснэгтүүдтэй ажилласан: тэдгээрийг уншиж, шүүж, өөрчилсөн. Тэр хүснэгтүүд бэлтгэлийн скриптээс эсвэл удирдамжтай дасгалын бэлэн тушаалаас үүссэн бөгөөд бид CREATE TABLE-ийг "одоохондоо ойлгох шаардлагагүй" гэж хойшлуулсаар ирсэн. Энэ түвшинд асуулт өөрчлөгдөнө: хүснэгт ямар байх ёстойг хэн шийддэг вэ, тэр шийдвэрийг хэрхэн зөв гаргах вэ?
Энэ хичээлд CREATE TABLE тушаалын бүтцийг бүрэн задалж, баганын гурван үндсэн төрөл — INTEGER, TEXT, REAL-тэй танилцана. Төрөл бүр ямар өгөгдөлд зориулагдсан, хэзээ алийг нь сонгох вэ гэдгийг жишээгээр харна. Мөн мөнгөн дүнг яагаад бутархай тоогоор хадгалж болохгүйг өөрийн нүдээр нотолно.
Хамгийн чухал нь, энэ хичээлд SQLite-ийн тухай нэг шударга үнэнийг хэлнэ: SQLite баганын төрлийг хатуу хууль биш, харин зөвлөмж гэж үздэг. Та тоо хадгалах баганад текст бичихэд SQLite ихэнхдээ эсэргүүцэхгүй. Энэ нь эхлэгчдэд тухтай мэт санагдаж болох ч чимээгүй алдааны эх үүсвэр болдог. Түвшин 4-т PostgreSQL руу шилжихэд төрөл бүр хатуу хууль болно — энэ хичээл тэр шилжилтэд бэлтгэх эхний алхам юм.
Бүртгэлийн дэвтэр шинээр хэвлэхийн өмнө маягтыг нь зохиох хэрэгтэй: багана бүрийн толгойд юу гэж бичих, багана бүрд ямар төрлийн мэдээлэл бичих вэ. "Нэр" баганад үг, "Он" баганад тоо, "Хувь" баганад бүхэл тоо. CREATE TABLE бол яг энэ маягт зохиох үйлдэл юм. Та хүснэгтийн нэр, багана бүрийн нэр, төрлийг нэг удаа зарлаж, дараа нь тэр маягтын дагуу мөр бүр бичигдэнэ.
Одоо хоёр өөр байгууллагыг төсөөлөөд үзээрэй. Эхнийх нь баганын толгойн доор харандаагаар "энд тоо бичнэ үү" гэж зөвлөмж бичсэн байдаг — хэн нэгэн тоон баганад "мэдэхгүй" гэж бичвэл хэн ч саатуулахгүй. Хоёр дахь нь хатуу нягтлантай бөгөөд тоон баганад үг бичсэн маягтыг тэр даруй буцаадаг. SQLite бол эхний байгууллага, PostgreSQL бол хоёр дахь нь. Аль аль нь маягттай, гэхдээ тэр маягтыг хэр хатуу мөрдүүлдэг нь өөр.
Бүлэг 1-ийн бэлтгэлийн скриптээс books хүснэгтийг үүсгэсэн тушаалыг эргэн харъя:
sql
CREATE TABLE books (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
author TEXT,
year INTEGER,
genre TEXT,
copies INTEGER
);Энэ тушаал юу гэж хэлж байна вэ гэвэл: “books нэртэй хүснэгт үүсгэ; түүнд id (бүхэл тоо, мөрийн давтагдашгүй дугаар), title (текст, хоосон байж болохгүй), author (текст), year (бүхэл тоо), genre (текст), copies (бүхэл тоо) гэсэн зургаан багана байна.”
Бүтцийг давхаргаар нь задалъя. CREATE TABLE-ийн араас хүснэгтийн нэр ирнэ. Нэрийн араас хаалт нээгдэж, хаалт дотор баганын тодорхойлолтууд таслалаар тусгаарлагдан жагсана. Тодорхойлолт бүр гурван хэсэгтэй: эхлээд баганын нэр (title), дараа нь төрөл (TEXT), сүүлд нь сонголттой дүрмүүд (NOT NULL, PRIMARY KEY). Хаалт хаагдаж, цэг таслалаар тушаал дуусна. Сүүлийн баганын араас таслал тавьдаггүй — SELECT-ийн баганын жагсаалт болон INSERT-ийн утгуудад сурсан дүрэм энд ч мөрдөгдөнө.
Баганын дүрмүүдийг (NOT NULL, PRIMARY KEY, мөн бусад) constraint гэж нэрлэдэг бөгөөд тэдгээрт дараагийн хоёр хичээлийг бүхэлд нь зориулна. Энэ хичээлд бид дунд хэсэг болох төрөл дээр анхаарна. Хүснэгт, баганы нэрийн хувьд нэг дадал зөвлөе: англи жижиг үсгээр, үгсийг доогуур зураасаар холбож бичээрэй (joined_year, watched_on). Кирилл үсэг, зай, том үсэг ашиглах техникийн боломж бий ч Python-оос, бусад хэрэгслээс ажиллахад олон жижиг асуудал үүсгэдэг.
INTEGER — бүхэл тоо: 0, 7, -15, 2026. Тоолж болох бүх зүйлд: хувийн тоо, он, нас, id. Бутархай хэсэггүй.
TEXT — текст: үг, өгүүлбэр, имэйл, огноо (YYYY-MM-DD хэлбэрээр). Утгыг дан хашилтаар бичдэг. Тоо шиг харагддаг ч тоо биш зүйл ч энд хамаарна — утасны дугаар, регистрийн дугаар, шуудангийн код. Тэдгээрийг хэзээ ч нэмж, хасдаггүй, мөн “0011” гэх мэт тэгээр эхэлдэг байж болно.
REAL — бутархай тоо: 1.5, 3.14, -0.25. Хэмжилтийн утгад: цаг хугацааны үргэлжлэл, жин, температур, дундаж үнэлгээ.
SQLite-д BLOB гэсэн дөрөв дэх төрөл (зураг гэх мэт хоёртын өгөгдөлд) бий ч энэ сургалтад бид түүнийг ашиглахгүй. Олон хүний хүлээх өөр хоёр төрөл SQLite-д байдаггүйг шударгаар хэлье: огноо болон үнэн/худал (boolean). Огноог өмнөх хичээлд хийсэн шиг TEXT-ээр, үнэн/худлыг INTEGER-ийн 1 ба 0-оор хадгалдаг. PostgreSQL-д эдгээрийн аль аль нь жинхэнэ төрөлтэй бөгөөд Бүлэг 10-д түүнтэй уулзана.
Номын сан арга хэмжээ — уншлагын клуб, сургалт, яриа — зохион байгуулж эхэллээ. Тэдгээрийг бүртгэх хүснэгт зохиоё. Арга хэмжээ бүрт нэр, оролцооны төлбөр (төгрөгөөр), үргэлжлэх хугацаа (цагаар, жишээ нь 1.5 цаг), огноо хэрэгтэй. library.db-г нээгээд Execute SQL табд ажиллуулаарай (энэ хичээлийн турш Write Changes дарахгүй, төгсгөлд Revert хийнэ):
sql
CREATE TABLE library_events (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
fee INTEGER,
duration_hours REAL,
event_date TEXT
);Энэ тушаал юу гэж хэлж байна вэ гэвэл: “library_events нэртэй хүснэгт үүсгэ; id нь бүхэл тоо бөгөөд давтагдашгүй дугаар, title нь хоосон байж болохгүй текст, fee нь бүхэл тоо, duration_hours нь бутархай тоо, event_date нь текст байна.” Төрөл бүрийн сонголтыг тайлбарлая. Төлбөр бүхэл тоо, учир нь төгрөгийн дүн бутархай хэсэггүй — 15000₮, 15000.5₮ биш. Хугацаа бутархай тоо, учир нь нэг цаг хагас (1.5) эсвэл нэг цаг арван таван минут (1.25) гэх мэт утга бодитоор гардаг. Огноо текст, учир нь SQLite-д огнооны төрөл байхгүй бөгөөд өмнөх хичээлд YYYY-MM-DD текст хэрхэн зөв эрэмбэлэгддэгийг харсан.
Одоо гурван арга хэмжээ нэмье:
sql
INSERT INTO library_events (title, fee, duration_hours, event_date)
VALUES
('Python уншлагын клуб', 0, 1.5, '2026-11-07'),
('SQL-ийн анхан шат', 15000, 2.0, '2026-11-14'),
('Монгол түүхийн яриа', 5000, 1.25, '2026-11-21');Хүснэгт (SELECT * FROM library_events;):
id | title | fee | duration_hours | event_date
---+----------------------+-------+----------------+-----------
1 | Python уншлагын клуб | 0 | 1.5 | 2026-11-07
2 | SQL-ийн анхан шат | 15000 | 2.0 | 2026-11-14
3 | Монгол түүхийн яриа | 5000 | 1.25 | 2026-11-21Энэ query юу гэж хэлж байна вэ гэвэл: “library_events хүснэгтэд гурван арга хэмжээг нэм.” Python уншлагын клубын төлбөр 0 гэж бичигдсэнийг анзаараарай — энэ нь NULL биш, учир нь бид төлбөр нь үнэгүй гэдгийг мэдэж байна. Бүлэг 2-ын NULL-ийн хичээлийн ялгаа энд ч хүчинтэй: 0 нь "төлбөргүй", NULL нь "төлбөрийг нь хараахан шийдээгүй."
Жишээ 1: утга бүрийн жинхэнэ төрлийг харах
SQLite нь утга бүрийг ямар төрлөөр хадгалсныг харуулдаг typeof() гэсэн функцтэй. Энэ хичээлд энэ функц бидний гол хэрэгсэл болно. Query:
sql
SELECT title, typeof(fee), typeof(duration_hours), typeof(event_date)
FROM library_events;Үр дүн:
title | typeof(fee) | typeof(duration_hours) | typeof(event_date)
---------------------+-------------+------------------------+-------------------
Python уншлагын клуб | integer | real | text
SQL-ийн анхан шат | integer | real | text
Монгол түүхийн яриа | integer | real | textЭнэ query юу гэж хэлж байна вэ гэвэл: “library_events хүснэгтийн мөр бүрийн хувьд title болон fee, duration_hours, event_date утгууд ямар төрлөөр хадгалагдсаныг харуул.” Бүх зүйл хүлээгдэж буйгаар: төлбөр integer, хугацаа real, огноо text. “SQL-ийн анхан шат”-ын хугацааг бид 2.0 гэж бичсэн бөгөөд энэ нь real-ээр хадгалагдсан — бутархай хэсэг нь тэг байсан ч REAL багана түүнийг бутархай тоо гэж хадгалсан. Одоогоор маягтын зөвлөмж болон бодит утгууд таарч байна. Дараагийн жишээнд энэ таарамж эвдэрнэ.
Жишээ 2: зөвлөмж ба хууль — SQLite-ийн сул төрөл
Хоёр шинэ арга хэмжээ нэмье. Эхнийх нь төлбөрийг санамсаргүй хашилттайгаар, хоёр дахь нь төлбөрийн оронд "үнэгүй" гэж бичсэн. Query:
sql
INSERT INTO library_events (title, fee, duration_hours, event_date)
VALUES
('Шатрын тэмцээн', '20000', 3, '2026-11-28'),
('Хүүхдийн үлгэрийн цаг', 'үнэгүй', 1, '2026-12-05');Мессеж: 2 rows affected. Алдаа гараагүй. Одоо төрлүүдийг шалгая:
sql
SELECT title, fee, typeof(fee), duration_hours, typeof(duration_hours)
FROM library_events
WHERE id > 3;Үр дүн:
title | fee | typeof(fee) | duration_hours | typeof(duration_hours)
----------------------+--------+-------------+----------------+-----------------------
Шатрын тэмцээн | 20000 | integer | 3.0 | real
Хүүхдийн үлгэрийн цаг | үнэгүй | text | 1.0 | realЭнэ query юу гэж хэлж байна вэ гэвэл: “id нь 3-аас их мөрүүдийн хувьд fee болон duration_hours-ийн утга, тэдгээрийн хадгалагдсан төрлийг харуул.” Хоёр өөр зүйл тохиолдсон. "Шатрын тэмцээн"-ий хувьд бид '20000' гэсэн текст өгсөн ч SQLite түүнийг INTEGER баганад зориулж бүхэл тоо 20000 болгон хөрвүүлсэн. Хугацааны 3 болон 1 гэсэн бүхэл тоонууд REAL баганад 3.0, 1.0 болсон. SQLite үүнийг type affinity — "төрлийн хандлага" гэж нэрлэдэг: багана өөрт нь өгсөн утгыг зарласан төрөл рүүгээ хөрвүүлэхийг оролддог.
Харин "Хүүхдийн үлгэрийн цаг"-ийн хувьд 'үнэгүй' гэдэг үгийг тоо руу хөрвүүлэх боломжгүй байсан. Энэ үед хатуу нягтлан маягтыг буцаах байсан бол SQLite зүгээр л текстийг текстээр нь хадгалсан. Одоо таны "бүхэл тоон" fee баганад нэг текстэн утга амьдарч байна. Зөвлөмжийг зөрчсөн боловч хэн ч саатуулаагүй.
Жишээ 3: чимээгүй алдааны үр дагавар
Холимог төрлийн багана хэрхэн буруу үр дүн өгдгийг харъя. 10000 төгрөгөөс илүү төлбөртэй арга хэмжээнүүдийг олъё. Query:
sql
SELECT title, fee
FROM library_events
WHERE fee > 10000;Үр дүн:
title | fee
----------------------+-------
SQL-ийн анхан шат | 15000
Шатрын тэмцээн | 20000
Хүүхдийн үлгэрийн цаг | үнэгүйЭнэ query юу гэж хэлж байна вэ гэвэл: “library_events хүснэгтээс fee нь 10000-аас их мөрүүдийн title болон fee-г харуул.” Эхний хоёр мөр зөв. Гэвч "Хүүхдийн үлгэрийн цаг" — үнэгүй арга хэмжээ — “10000-аас илүү үнэтэй” гэж гарч ирлээ. Шалтгаан нь SQLite-ийн нэг дүрэм: тоо ба текстийг харьцуулахад текст үргэлж аливаа тооноос их гэж тооцогддог. Тиймээс 'үнэгүй' > 10000 нь үнэн. Алдааны мессеж гараагүй, query ажилласан, үр дүн нь худлаа.
Энэ төрлийн алдааг төсөөлж үзээрэй: номын сангийн сайт "эцэг эхчүүдэд: үнэтэй арга хэмжээнүүд" гэсэн хэсэгт хүүхдийн үнэгүй үлгэрийн цагийг харуулж байна. Эсвэл сарын орлогын тайланд SUM(fee) хийхэд энэ текст хачин үр дүн өгнө. Буруу утга хадгалагдсан тэр мөчид асуудал эхэлсэн ч илэрсэн нь хэдэн долоо хоногийн дараа, өөр query-д байх магадлалтай. Энэ бол яг ийм шалтгаанаар мэргэжлийн өгөгдлийн сангууд төрлийг хатуу мөрдүүлдэг.
PostgreSQL-д ижил INSERT хийвэл invalid input syntax for type integer: "үнэгүй" гэсэн алдаа гарч, мөр огт нэмэгдэхгүй. Энэ мессеж нь "бүхэл тоон төрөлд 'үнэгүй' гэдэг оролт хүчингүй" гэсэн утгатай бөгөөд Бүлэг 10-д түүнийг өөрийн нүдээр харна. Мөн SQLite-ийн шинэ хувилбаруудад хүснэгтийн төгсгөлд STRICT гэдэг үг нэмж PostgreSQL-тэй адил хатуу төрөл мөрдүүлэх боломж бий. Гэвч энэ нь SQLite-ийн өргөн хэрэглэгддэг хэлбэр биш тул энэ сургалт түүнийг ашиглахгүй — хатуу төрлийн жинхэнэ дадлыг PostgreSQL дээр сурна. Одоохондоо SQLite дээр хамгаалалт нь таны өөрийн анхаарал: баганын төрөлд тохирох утгыг л бичих.
Жишээ 4: яагаад мөнгийг REAL-ээр хадгалж болохгүй вэ
"Төлбөрийг REAL-ээр хадгалбал илүү уян хатан биш үү, хэрэв хэзээ нэгэн цагт 15000.50 гэх мэт дүн гарвал?" гэсэн асуулт төрж болно. Хариултыг нэг энгийн query харуулна:
sql
SELECT 0.1 + 0.2 = 0.3;Үр дүн:
0.1 + 0.2 = 0.3
---------------
0Энэ query юу гэж хэлж байна вэ гэвэл: “0.1 дээр 0.2 нэмсэн нь 0.3-тай тэнцүү юу?” SQLite 0 буюу "худал" гэж хариулжээ. Энэ бол SQLite-ийн алдаа биш — компьютер бутархай тоог хоёртын системд хадгалдаг бөгөөд 0.1 гэх мэт олон энгийн аравтын бутархайг яг таг илэрхийлэх боломжгүй. Тэдгээр нь маш бага зөрүүтэй ойролцоо утгаар хадгалагддаг бөгөөд нэмэхэд тэр жижиг зөрүүнүүд хуримтлагддаг. Python-д 0.1 + 0.2 == 0.3 гэж бичвэл ч мөн адил False гарна — энэ нь бүх програмчлалын хэлэнд нийтлэг үзэгдэл юм.
Хугацааны хувьд ийм жижиг зөрүү хамаагүй: 1.5 цагийн оронд 1.4999999999999998 цаг байх нь хэнд ч саад болохгүй. Харин мөнгөнд энэ нь хүлээн зөвшөөрөгдөхгүй: мянга мянган гүйлгээний нийлбэр хэдэн мөнгөөр зөрөх, эсвэл "төлсөн дүн нь төлөх дүнтэй тэнцүү юу" гэсэн шалгалт худал гарах аюултай. Тиймээс мэргэжлийн системүүдэд мөнгийг үргэлж бүхэл тоогоор, хамгийн жижиг нэгжээр нь хадгалдаг: доллар бол центээр, төгрөг бол төгрөгөөр. Манай fee баганыг INTEGER гэж сонгосон нь яг энэ шалтгаантай. Дүрэм энгийн: тоолдог зүйл — INTEGER, хэмждэг зүйл — REAL, мөнгө — үргэлж INTEGER.
Бид Write Changes дараагүй тул Revert Changes дарахад library_events хүснэгт болон түүний бүх мөр алга болно. Database Structure табд зөвхөн books, members үлдсэн эсэхийг шалгаарай.
Хэрэв сонирхож байвал өөрийн сонирхолд нийцсэн хүснэгт зохиож үзээрэй — жишээ нь гүйлтийн бүртгэл (огноо, зай километрээр, хугацаа минутаар, тэмдэглэл) эсвэл гэрийн төсөв (огноо, зардлын төрөл, дүн төгрөгөөр). Багана бүрд төрөл сонгохдоо "үүнийг тоолдог уу, хэмждэг үү, эсвэл зүгээр нэр, тэмдэглэл үү" гэж өөрөөсөө асуугаарай. Дараа нь хэдэн мөр нэмж, typeof()-оор хадгалагдсан төрлийг шалгаарай. Дуусаад Revert Changes дарж болно. Эдгээр нь зөвхөн санал бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж тооцохгүй.
Сүүлийн баганын араас таслал. event_date TEXT, гэж сүүлийн мөрөнд таслал үлдээгээд хаалтаа хаавал near ")": syntax error гарна. Энэ мессеж юу гэж хэлж байна вэ гэвэл: "хаах хаалтын ойролцоо SQL-ийн дүрэм зөрчигдлөө." SQLite таслалын араас дахин нэг баганын тодорхойлолт хүлээж байтал хаалт ирсэн. Баганаа хасах эсвэл дарааллыг солих үед энэ алдаа маш элбэг гардаг.
Хоёр баганын хооронд таслал мартах. title TEXT NOT NULL болон fee INTEGER-ийн хооронд таслал мартвал near "fee": syntax error гарна. Утга нь “fee гэдэг үгийн ойролцоо дүрэм зөрчигдлөө.” SQLite title-ийн тодорхойлолт үргэлжилж байна гэж бодоод араас нь дахин нэг дүрэм хүлээж байтал баганын нэр ирсэн. Урт CREATE TABLE-ийг багана бүрийг тусдаа мөрөнд бичих нь иймэрхүү алдааг нүдээр олоход хялбар болгодог.
Хүснэгт аль хэдийн байх. Ижил CREATE TABLE-ийг хоёр удаа ажиллуулбал table library_events already exists гарна. Утга нь “library_events хүснэгт аль хэдийн байна.” Хэрэв та хүснэгт байхгүй үед л үүсгэхийг хүсвэл CREATE TABLE IF NOT EXISTS library_events (...) гэж бичиж болно — энэ нь бэлтгэлийн скриптэд ашигласан DROP TABLE IF EXISTS-ийн эсрэг тал юм. FastAPI сургалтын create_all() дотроо яг ингэж ажилладаг байсан: хүснэгт байвал алгасаад, байхгүй бол үүсгэдэг.
Төрлийн нэрийг буруу бичих. SQLite нь танихгүй төрлийн нэрийг ч хүлээн авдаг бөгөөд нэрэнд нь ямар үсэг байгаагаар хандлагыг таамагладаг. fee INTEGR гэж бичвэл “INT” гэсэн хэсэг агуулж байгаа тул санамсаргүй зөв ажиллана. Харин phone TXT гэж бичвэл “TEXT” гэдэг үг таниулагдахгүй тул SQLite тоон хандлага сонгоно — тэгээд '0011' гэх мэт утасны дугаар 11 гэсэн тоо болж, эхний тэгүүд чимээгүйхэн алга болно. Алдааны мессеж гарахгүй. Төрлийн нэрээ INTEGER, TEXT, REAL гэж яг таг бичих нь энэ асуудлаас бүрэн хамгаална.
Урт хязгаарт итгэх. Бусад өгөгдлийн сангаас ирсэн VARCHAR(50) гэх мэт төрлийг SQLite хүлээн авч TEXT гэж үздэг, гэхдээ 50 тэмдэгтийн хязгаарыг огт мөрдүүлдэггүй. Хэрэв та ийм баганад мянган тэмдэгт бичвэл SQLite бүгдийг нь хадгална. PostgreSQL-д энэ хязгаар жинхэнэ хууль бөгөөд хэтэрвэл алдаа гарна. Тиймээс SQLite-д уртын хязгаар тавьсан мэт харагдах тодорхойлолтод бүү найдаарай.
Энэ хичээлд та CREATE TABLE тушаалын бүтцийг бүрэн ойлголоо: хүснэгтийн нэр, хаалт дотор таслалаар тусгаарласан баганын тодорхойлолтууд, тодорхойлолт бүр нэр, төрөл, сонголттой дүрмүүдээс бүрдэнэ. Гурван үндсэн төрлийг сурлаа: тоолдог зүйлд INTEGER, үг ба тоо шиг харагддаг ч тоо биш зүйлд TEXT, хэмждэг зүйлд REAL — харин мөнгийг бутархай тооны нарийвчлалгүй байдлаас болж үргэлж INTEGER-ээр. SQLite-ийн төрөл нь хууль биш зөвлөмж бөгөөд type affinity-ээр утгыг хөрвүүлэхийг оролдож, бүтэхгүй бол буруу төрлийг чимээгүй хадгалдгийг, түүнээс болж query буруу үр дүн өгдгийг typeof()-оор өөрийн нүдээр харлаа. PostgreSQL-д төрөл хатуу мөрдөгдөнө.
Дараагийн хичээлд бид баганын тодорхойлолтын гурав дахь хэсэг — constraint-уудыг гүнзгий судална. NOT NULL, UNIQUE, DEFAULT, CHECK гэсэн дөрвөн дүрэм нь SQLite-ийн сул төрлөөс ялгаатай нь жинхэнэ хууль шиг мөрдөгддөг. Тэдгээр нь энэ хичээлд харсан "үнэгүй" гэх мэт буруу өгөгдлийг хүснэгтэд нэвтрэхээс өмнө зогсоох хамгийн хүчтэй хэрэгсэл юм. Бид дүрэм бүрийг санаатайгаар зөрчиж, алдааны мессежийг нь уншина.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.