Ачааллж байна...
Энэ сургалтын бүх query өнөөг хүртэл агшин зуур ажилласан. Манай library2.db найман ном, таван гишүүн, найман зээлтэй — ийм жижиг хүснэгтэд ямар ч query хурдан. Гэвч бодит номын сан олон жилийн зээлийн түүхтэй: хэдэн зуун мянга, магадгүй сая сая мөр. Тэр хэмжээнд "энэ гишүүний бүх зээлийг ол" гэсэн энгийн query мэдэгдэхүйц удаан болж, вэб сайтын хуудас нээгдэхийг хэрэглэгч хүлээж эхэлнэ.
Энэ хичээлд бид хурдны асуудлыг үгээр биш, хэмжилтээр судална. Эхлээд скриптээр сая мөртэй хүснэгт үүсгэнэ. Дараа нь нэг query-ний хугацааг хэмжиж, өгөгдлийн сан түүнийг хэрхэн гүйцэтгэж байгааг өөрөөс нь асууна. Үүний дараа нэг мөр тушаалаар index нэмж, ижил query-г дахин хэмжинэ. Ялгаа нь гайхмаар том байх болно.
Гэхдээ энэ бол хагас түүх. Index үнэгүй биш: тэр дискэн дээр зай эзэлдэг, мөр нэмэх, засах, устгах бүрийг удаашруулдаг. Бүлэг 6-д "хурдны төлөө denormalization-ийг хурдны асуудал хэмжигдэхээс өмнө бүү хий; Бүлэг 9-д ихэнх хурдны асуудлыг давхардалгүйгээр шийдэх аргыг харна" гэж хэлсэн — index бол тэр арга. Энэ хичээлд бид түүний ашиг тусыг ч, үнийг ч хоёуланг нь хэмжиж, хаана index тавих, хаана тавихгүй байх практик дүрмийг гаргана.
FastAPI сургалтад index-ийг номын товьёогтой зүйрлэж байсан бол одоо тэр үрийг ургуулъя. Зузаан сурах бичигт “transaction” гэдэг үг аль хуудсанд гардгийг олох хоёр арга бий. Эхнийх нь эхний хуудаснаас эхлэн нэг бүрчлэн гүйлгэж, үг бүрийг шалгах — ном хэдий чинээ зузаан, төдий чинээ удаан. Хоёр дахь нь номын арын товьёог руу эргэх: тэнд үгс цагаан толгойн дарааллаар жагсаж, үг бүрийн хажууд хуудасны дугаар бичигдсэн. “T” үсэг рүү үсрээд, “transaction”-ыг хормын дотор олж, “143, 287” гэсэн хуудас руу шууд очно.
Өгөгдлийн сан index-гүй хүснэгтээс мөр хайхдаа эхний аргыг хэрэглэдэг: мөр бүрийг дарааллаар нь шалгана. Үүнийг scan буюу "гүйлгэх" гэдэг. Index бол товьёог: тодорхой баганын утгуудыг эрэмбэлэн хадгалж, утга бүрийн хажууд тэр утгатай мөрүүд хаана байгааг тэмдэглэсэн тусдаа бүтэц. Index-тэй үед өгөгдлийн сан "гүйлгэх"-ийн оронд "хайх" (search) боломжтой болно.
Зүйрлэлд үнийн тал ч бий. Товьёог номын хуудас нэмдэг — номыг зузаан болгодог. Мөн зохиогч номдоо шинэ догол мөр нэмэх бүрт товьёогийг ч шинэчлэх ёстой. Товьёог олон байх тусам — үг бүрийн товьёог, нэр бүрийн товьёог, огноо бүрийн товьёог — засвар бүр улам удаан болно. Index-ийн бүх шийдвэр энэ тэнцвэр дээр суурилдаг.
Бид library2.db-д хүрэхгүй — тэр нь жижиг, цэвэр хэвээр байх ёстой. Тусдаа файл үүсгэнэ:
DB Browser-т хадгалаагүй өөрчлөлт байхгүй эсэхийг шалгаад New Database дарна.
Documents\sql-course хавтсанд big нэрээр хадгална. Edit table definition цонхонд Cancel.
Одоо Execute SQL табд дараах скриптийг ажиллуулна. Энэ скрипт сая зээлийн түүхийн мөр үүсгэнэ — 50,000 гишүүн, 20,000 ном, 2020-2025 оны хооронд санамсаргүй огноотой. Скриптийн хоёр дахь хэсэг бидний сураагүй WITH RECURSIVE гэсэн бичлэгийг ашигладаг (энэ нь 1-ээс 1,000,000 хүртэлх тоог үүсгэх арга) бөгөөд random() нь санамсаргүй тоо өгдөг. Бүлэг 1-ийн бэлтгэлийн скриптийн нэгэн адил одоохондоо үүнийг "бэлэн хэрэгсэл" гэж хүлээн авахад хангалттай.
sql
CREATE TABLE loan_history (
id INTEGER PRIMARY KEY,
member_id INTEGER NOT NULL,
book_id INTEGER NOT NULL,
loaned_on TEXT NOT NULL
);
WITH RECURSIVE n(i) AS (
SELECT 1
UNION ALL
SELECT i + 1 FROM n WHERE i < 1000000
)
INSERT INTO loan_history (member_id, book_id, loaned_on)
SELECT abs(random()) % 50000 + 1,
abs(random()) % 20000 + 1,
date('2020-01-01', '+' || (abs(random()) % 2190) || ' days')
FROM n;Ажиллахад хэдэн секунд зарцуулна. Мессеж нь 1000000 rows affected гэх мэт байх ёстой. Дараа нь Write Changes дарна — дараа нь файлын хэмжээг хэмжинэ.
Шалгах цэг 1: SELECT COUNT(*) FROM loan_history; нь 1000000 гэж хариулна. SELECT * FROM loan_history LIMIT 5; нь санамсаргүй тоо, огноотой таван мөр харуулна — таны мөрүүд надаас өөр байна, учир нь өгөгдөл санамсаргүй. Мөн File Explorer-оор big.db-ийн хэмжээг хараарай — ойролцоогоор 20-30 МБ. Энэ тоог тэмдэглэж аваарай.
Хэмжилт 1: index-гүй
Нэг гишүүний зээлийн тоог олъё. Query:
sql
SELECT COUNT(*) FROM loan_history WHERE member_id = 12345;Үр дүн нь 20 орчим тоо (санамсаргүй өгөгдөл тул таных өөр), харин бидэнд хэрэгтэй нь мессежийн талбар. Тэнд 1 rows returned in 52ms гэх мэт мөр гарна. Энэ query юу гэж хэлж байна вэ гэвэл: “loan_history-ээс member_id нь 12345 мөрүүдийг тоол.” Энэ query-г гурван удаа ажиллуулж, хугацааг тэмдэглээрэй — компьютер бүр өөр хурдтай тул таны тоо 20ms ч байж болно, 150ms ч байж болно. Тоо нь өөрөө чухал биш, харин дараагийн хэмжилттэй харьцуулах нь чухал.
Одоо өгөгдлийн сангаас энэ query-г хэрхэн гүйцэтгэж байгааг асууя. Query-ний өмнө EXPLAIN QUERY PLAN гэж нэмбэл SQLite query-г ажиллуулахын оронд төлөвлөгөөгөө тайлбарлана:
sql
EXPLAIN QUERY PLAN
SELECT COUNT(*) FROM loan_history WHERE member_id = 12345;Үр дүн:
SCAN loan_historyЭнэ мөр юу гэж хэлж байна вэ гэвэл: “loan_history хүснэгтийг эхнээс нь дуустал гүйлгэнэ.” SQLite сая мөр бүрийг уншиж, member_id нь 12345 эсэхийг шалгах гэж байна — тэдгээрийн 20 орчим нь л таарна. Зүйрлэлийн "эхний хуудаснаас гүйлгэх" арга. EXPLAIN QUERY PLAN нь хугацааны хэмжилтээс илүү найдвартай нотолгоо, учир нь компьютерийн хурдаас үл хамааран өгөгдлийн сангийн бодит шийдвэрийг харуулдаг.
Index үүсгэх
Query:
sql
CREATE INDEX idx_loan_history_member ON loan_history (member_id);Энэ тушаал юу гэж хэлж байна вэ гэвэл: “loan_history хүснэгтийн member_id баганын idx_loan_history_member гэсэн нэртэй index үүсгэ.” Бүтэц: CREATE INDEX, index-ийн нэр, ON, хүснэгт, хаалт дотор багана. Нэрийг idx_хүснэгт_багана хэлбэрээр өгөх нь тогтсон дадал — дараа нь Database Structure табд харахад юуны index болохыг шууд мэднэ. Сая мөрийн index үүсгэхэд нэг хоёр секунд зарцуулна: SQLite сая утгыг эрэмбэлж, товьёог бүтээж байна. Write Changes дарна.
Хэмжилт 2: index-тэй
Ижил query-г дахин ажиллуулна:
sql
SELECT COUNT(*) FROM loan_history WHERE member_id = 12345;Үр дүнгийн тоо өөрчлөгдөөгүй, харин мессеж: 1 rows returned in 0ms эсвэл 1ms. Төлөвлөгөөг асууя:
sql
EXPLAIN QUERY PLAN
SELECT COUNT(*) FROM loan_history WHERE member_id = 12345;Үр дүн:
SEARCH loan_history USING COVERING INDEX idx_loan_history_member (member_id=?)Энэ мөр юу гэж хэлж байна вэ гэвэл: “loan_history-д idx_loan_history_member index-ийг ашиглан member_id нь өгсөн утгатай мөрүүдийг хайна.” SCAN-ийн оронд SEARCH — гүйлгэхийн оронд хайх. COVERING гэдэг үг нь “index дангаараа хангалттай” гэсэн утгатай: COUNT(*)-д зөвхөн member_id хэрэгтэй тул SQLite хүснэгт рүү огт эргэлгүй, зөвхөн товьёогийг уншиж хариулсан. (member_id=?) нь хайлтын нөхцлийг харуулна.
Хоёр хэмжилтийг зэрэгцүүлье:
| index-гүй | index-тэй
------------------------+------------------+---------------------------------
Төлөвлөгөө | SCAN | SEARCH USING COVERING INDEX
Шалгасан мөр | 1,000,000 | ~20
Хугацаа (жишээ) | 52ms | 0msСая мөрийн оронд хорин мөр. Хугацааны ялгаа дор хаяж хэдэн арав дахин, ихэнхдээ хэдэн зуу дахин. Энэ query-г вэб сайт секунд тутам олон удаа ажиллуулдаг бол ялгаа нь "хуудас хурдан нээгддэг" ба “server ачааллаа даахгүй” хоёрын ялгаа. Хамгийн чухал нь query өөрөө огт өөрчлөгдөөгүй — бид ганц ч үг засаагүй. Index нь query-ний утгыг биш, зөвхөн хурдыг өөрчилдөг. Тиймээс index-ийг дараа нь нэмэх эсвэл хасах нь програмын кодыг эвддэггүй — Бүлэг 4-ийн ALTER-ийн нэр солих асуудлаас огт ялгаатай.
Жишээ 1: аль хэдийн байгаа index-үүд
Та нэг index үүсгэсэн ч SQLite өөрөө хэдэн index-ийг аль хэдийн үүсгэсэн байдаг. PRIMARY KEY нь өөрөө index: WHERE id = 500000 гэх мэт query SEARCH loan_history USING INTEGER PRIMARY KEY (rowid=?) гэсэн төлөвлөгөөтэй — Бүлэг 4-т сурсан rowid нь хүснэгтийн дотоод товьёог. Мөн UNIQUE constraint бүр нууцаар index үүсгэдэг, учир нь давхардлыг шалгахын тулд өгөгдлийн сан утгыг хурдан хайх хэрэгтэй. library2.db-д members.email ба books.isbn UNIQUE тул Database Structure табд sqlite_autoindex_members_1 гэх мэт автомат index-үүд харагдана.
Харин FOREIGN KEY баганад SQLite автоматаар index үүсгэдэггүй — PostgreSQL ч мөн адил. library2.db-ийн loans.member_id, loans.book_id index-гүй. Тэдгээр нь Бүлэг 7-8-ын бүх JOIN-д тааруулах багана болж байсан. Найман мөртэй хүснэгтэд хамаагүй, харин сая мөрт энэ нь JOIN бүрийг scan болгоно. Тиймээс практикийн хамгийн чухал дүрэм: FOREIGN KEY баганууд, ялангуяа JOIN-д хэрэглэгддэг бол, ихэнхдээ index хэрэгтэй.
Жишээ 2: нийлмэл index ба баганын дараалал
Index нэгээс олон баганатай байж болно. "Энэ гишүүний зээлүүдийг огноогоор нь" гэсэн query түгээмэл:
sql
CREATE INDEX idx_loan_history_member_date ON loan_history (member_id, loaned_on);
EXPLAIN QUERY PLAN
SELECT loaned_on FROM loan_history
WHERE member_id = 12345
ORDER BY loaned_on DESC;Үр дүн:
SEARCH loan_history USING COVERING INDEX idx_loan_history_member_date (member_id=?)Энэ тушаалууд юу гэж хэлж байна вэ гэвэл: “member_id ба loaned_on-оор нийлмэл index үүсгэ; дараа нь нэг гишүүний зээлсэн огноонуудыг сүүлээс нь эхлэн харуулах query-ний төлөвлөгөөг харуул.” Төлөвлөгөөнд “TEMP B-TREE FOR ORDER BY” (тусад нь эрэмбэлэх ажил) гэсэн мөр байхгүйг анзаараарай: нийлмэл index утгуудыг эхлээд member_id-ээр, дотор нь loaned_on-оор эрэмбэлэн хадгалдаг тул нэг гишүүний огноонууд аль хэдийн эрэмбэлэгдсэн байдалтай. Утасны дэвтэр шиг: овгоор, дотор нь нэрээр.
Энэ зүйрлэл нийлмэл index-ийн хязгаарыг ч харуулна. Утасны дэвтэр "Батбаяр овогтой хүмүүсийг" хурдан олоход тустай, харин зөвхөн "Дорж нэртэй хүмүүсийг" олоход огт тусгүй — нэрс овог бүрийн дотор тархсан. Ижилээр (member_id, loaned_on) index нь WHERE member_id = ...-д тусална, харин зөвхөн WHERE loaned_on = ...-д тусалдаггүй. Нийлмэл index-ийн баганын дараалал чухал: эхний баганаар шүүдэг query-нүүдэд л ашиг өгнө.
Жишээ 3: index-ийг "эвддэг" бичлэг
Огнооны index үүсгээд, 2024 оны 3-р сарын зээлүүдийг хоёр өөр аргаар тоолъё:
sql
CREATE INDEX idx_loan_history_date ON loan_history (loaned_on);
EXPLAIN QUERY PLAN
SELECT COUNT(*) FROM loan_history
WHERE substr(loaned_on, 1, 7) = '2024-03';Үр дүн:
SCAN loan_history USING COVERING INDEX idx_loan_history_datesql
EXPLAIN QUERY PLAN
SELECT COUNT(*) FROM loan_history
WHERE loaned_on BETWEEN '2024-03-01' AND '2024-03-31';Үр дүн:
SEARCH loan_history USING COVERING INDEX idx_loan_history_date (loaned_on>? AND loaned_on<?)Эдгээр query хоёулаа “2024 оны 3-р сард зээлсэн мөрүүдийг тоол” гэж хэлж байгаа бөгөөд ижил тоо буцаана. Гэвч эхнийх нь SCAN — index-ийг бүхэлд нь гүйлгэж байна (хүснэгтээс арай хурдан, гэхдээ сая утгыг шалгасаар). Хоёр дахь нь SEARCH — товьёогийн 3-р сарын хэсэг рүү шууд үсэрч байна. Ялгаа нь: эхний query баганыг substr() функцэд оруулсан. Index нь loaned_on-ий утгуудыг эрэмбэлсэн, харин substr(loaned_on, 1, 7)-ийн утгуудыг биш — өгөгдлийн сан мөр бүрт функцийг ажиллуулж байж л харьцуулж чадна. Товьёог “transaction” гэдэг үгийг олоход тусалдаг ч "сүүлийн гурван үсэг нь ‘ion’" гэсэн хайлтад тусалдаггүй.
Энэ нь Бүлэг 8-ын GROUP BY хичээлийн substr() жишээг буруу болгодоггүй — тэнд бид бүх сарыг тоолсон тул ямар ч байсан бүх мөрийг унших хэрэгтэй байсан. Харин нэг тодорхой хүрээг шүүх үед баганыг "нүцгэн" үлдээж, хүрээг BETWEEN эсвэл >=, <-ээр бичих нь index-ийг ажиллуулна. Үүнтэй адил WHERE title LIKE '%Code%' (эхэндээ %) нь index ашиглаж чадахгүй — товьёог эхлэлээр эрэмбэлэгддэг, дундаас нь биш.
Жишээ 4: index-ийн үнэ — бичих хурд ба зай
Одоо шударга талыг хэмжье. loan_history одоо гурван index-тэй (member_id; member_id ба loaned_on; loaned_on). Эхлээд файлын хэмжээг шалгая: Write Changes дарсан эсэхийг шалгаад File Explorer-оор big.db-г хараарай. Хэмжээ нь Шалгах цэг 1-ийнхээс мэдэгдэхүйц томорсон байна — ихэнхдээ хоёр дахин орчим. Index бүр сая утгыг өөрийн эрэмбэлсэн хуулбартайгаар хадгалдаг; товьёог номыг зузаан болгосон.
Дараа нь бичих хурдыг хэмжье. Нэмэлт 100,000 мөр оруулъя:
sql
WITH RECURSIVE n(i) AS (
SELECT 1 UNION ALL SELECT i + 1 FROM n WHERE i < 100000
)
INSERT INTO loan_history (member_id, book_id, loaned_on)
SELECT abs(random()) % 50000 + 1,
abs(random()) % 20000 + 1,
date('2020-01-01', '+' || (abs(random()) % 2190) || ' days')
FROM n;Мессеж дэх хугацааг тэмдэглэ (жишээ нь 420ms). Одоо Revert Changes дарж энэ 100,000 мөрийг цуцлаад, гурван index-ийг устгая:
sql
DROP INDEX idx_loan_history_member;
DROP INDEX idx_loan_history_member_date;
DROP INDEX idx_loan_history_date;Ижил INSERT скриптийг дахин ажиллуулж хугацааг тэмдэглэ (жишээ нь 130ms). Хоёр тоог харьцуулаарай:
| гурван index-тэй | index-гүй
--------------------------+------------------+-----------
100,000 мөр нэмэх (жишээ) | 420ms | 130msТаны тоонууд өөр байх ч чиглэл ижил: index-тэй хүснэгтэд бичих нь илүү удаан. Учир нь шинэ мөр бүр хүснэгтэд нэмэгдээд зогсохгүй, гурван товьёог тус бүрт зөв эрэмбэлсэн байрандаа шигдэх ёстой. UPDATE ба DELETE-д ч ижил үнэ үйлчилнэ. Index бол унших хурдыг бичих хурдаар худалдан авах арилжаа юм. Энэ хэмжилтийн дараа Revert Changes дарж болно.
Хэмжилтүүдээс практик дүрэм гаргая.
Index тавих:
Байнга WHERE-д шүүгддэг, ялангуяа цөөн мөр олдог (member_id = 12345 — саяас хорь) багана.
JOIN-д тааруулах FOREIGN KEY багана.
Байнга ORDER BY-д эрэмбэлэгддэг, ялангуяа LIMIT-тэй top-N query-д.
Index тавихгүй:
Жижиг хүснэгт. Хэдэн зуун мөртэй хүснэгтийг scan хийх нь index хайлтаас бараг ялгаагүй хурдан бөгөөд index зөвхөн бичих зардал нэмнэ. library2.db-ийн хүснэгтүүдэд index хэрэггүй.
Цөөн өөр утгатай багана. members.is_active зөвхөн 0 ба 1 утгатай — WHERE is_active = 1 нь хүснэгтийн бараг бүх мөрийг олох тул товьёог тусгүй, өгөгдлийн сан ихэнхдээ scan сонгоно. Хүйс, төлөв гэх мэт баганууд ижил.
Ховор query-д хэрэглэгддэг багана. Сард нэг удаа ажилладаг тайлангийн төлөө index тавих нь өдөр бүрийн мянга мянган бичилтийг удаашруулна.
Бичилт ихтэй, уншилт бага хүснэгт — жишээ нь секунд тутам шинэ мөр нэмэгддэг лог хүснэгт.
Хамгийн чухал дүрэм нь Бүлэг 6-ийн denormalization-ий дүрэмтэй ижил: хэмжилтээр эхэл. "Энэ query удаан байна" гэдгийг бодитоор хэмж, EXPLAIN QUERY PLAN-аар SCAN байгааг нотол, index нэмээд дахин хэмж. "Ирээдүйд хэрэг болж магадгүй" гэсэн таамгаар index тавих нь бичилтийн зардал, файлын хэмжээг дэмий өсгөдөг.
Нэмэлт нэг хэрэгтэй мэдээлэл: CREATE UNIQUE INDEX нь index үүсгэхийн зэрэгцээ тэр баганын давхардлыг хориглодог — UNIQUE constraint-тай яг ижил үүрэг. Бүлэг 4-ийн ALTER хичээлд SQLite байгаа хүснэгтэд UNIQUE багана нэмэхийг зөвшөөрдөггүй гэж сурсан. Харин байгаа баганад UNIQUE INDEX дараа нь нэмж болно — хүснэгтийг дахин бүтээхгүйгээр давхардлын хуулийг тавих арга.
Хэмжилт дууслаа. DB Browser-т File → Close Database дарж (хадгалах эсэхийг асуувал аль нь ч болно), File Explorer-оор Documents\sql-course\big.db файлыг устгаж болно — хэдэн арван мегабайт зай чөлөөлөгдөнө. Хэрэв дахин туршихыг хүсвэл Алхам 1-ийн скрипт хэдэн секундэд шинээр үүсгэнэ. library2.db энэ хичээлд огт хөндөгдөөгүй.
Хэрэв сонирхож байвал big.db-г дахин үүсгээд, номын id-аар хайх query (WHERE book_id = 777)-г index-гүй ба index-тэй хэмжиж үзээрэй. Мөн JOIN-ий хурдыг шалгаж болно: members_big гэсэн 50,000 мөртэй хүснэгтийг ижил аргаар үүсгэж, loan_history-тэй member_id-аар нийлүүлж, нэг гишүүний мэдээллийг шүүх query-ний EXPLAIN QUERY PLAN-ийг index-гүй ба index-тэй харьцуулаарай. Эцэст нь WHERE member_id + 0 = 12345 гэх мэт баганыг илэрхийлэлд "нуусан" query-ний төлөвлөгөөг хараарай — Жишээ 3-ын дүрэм тоон баганад ч хамаарна. Эдгээр нь зөвхөн санал бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж тооцохгүй.
Ижил нэртэй index-ийг дахин үүсгэх. CREATE INDEX-ийг хоёр удаа ажиллуулбал index idx_loan_history_member already exists гарна. Энэ мессеж юу гэж хэлж байна вэ гэвэл: “idx_loan_history_member index аль хэдийн байна.” CREATE INDEX IF NOT EXISTS ... гэж бичвэл байхгүй үед л үүсгэнэ — Бүлэг 4-ийн CREATE TABLE IF NOT EXISTS-тэй адил.
Index тавьсан ч SCAN хэвээр. EXPLAIN QUERY PLAN SCAN гэж хэлсээр байвал Жишээ 3-ын шалтгаануудыг шалгаарай: баганыг функцэд оруулсан уу (substr, lower, арифметик)? LIKE эхэндээ %-тэй юу? Нийлмэл index-ийн эхний баганаар шүүж байна уу? Эсвэл баганын утга цөөн тул өгөгдлийн сан scan-ийг санаатайгаар сонгосон уу?
Хугацааны нэг хэмжилтэд итгэх. Query-г анх удаа ажиллуулахад өгөгдөл дискнээс уншигддаг тул удаан, дараагийн удаа санах ойд байдаг тул хурдан байж болно. Хэмжилт бүрийг хэд хэдэн удаа давтаж, тогтвортой тоог харьцуул, эсвэл компьютерийн хурдаас үл хамаарах EXPLAIN QUERY PLAN-д найд.
Бүх баганад index тавих. Жишээ 4-т харсанчлан index бүр бичилтийг удаашруулж, зай эзэлдэг. "Илүүдэл хор хөнөөлгүй" гэсэн бодол index-ийн хувьд буруу. Index бүр хэмжсэн, бодит query-ний хэрэгцээгээр зөвтгөгдсөн байх ёстой.
Index-ийг query засах хэрэгсэл гэж ойлгох. Index нь буруу query-г зөв болгодоггүй — зөвхөн хурдасгадаг. Бүлэг 7-ийн ON-гүй JOIN-ий тэсрэлт эсвэл Бүлэг 8-ын мөр үржилтийг index засахгүй; тэдгээр нь утгын алдаа, хурдных биш.
Энэ хичээлд та index-ийг хэмжилтээр судаллаа. Index-гүй үед өгөгдлийн сан хүснэгтийг эхнээс нь дуустал гүйлгэдэг (SCAN), харин CREATE INDEX нэр ON хүснэгт (багана) нь баганын утгуудыг эрэмбэлсэн товьёог үүсгэж, хайлтыг (SEARCH) боломжтой болгодог — сая мөрийн хүснэгтэд хэдэн зуу дахин хурдан. EXPLAIN QUERY PLAN нь компьютерийн хурдаас үл хамааран өгөгдлийн сангийн шийдвэрийг харуулдаг найдвартай нотолгоо. PRIMARY KEY ба UNIQUE автоматаар index-тэй, харин FOREIGN KEY баганууд тийм биш тул JOIN-д хэрэглэгдвэл index хэрэгтэй. Нийлмэл index-ийн баганын дараалал чухал; баганыг функцэд оруулах эсвэл LIKE-ийг %-ээр эхлүүлэх нь index-ийг ашиглах боломжгүй болгодог. Index нь бичилтийг удаашруулж, зай эзэлдэг тул жижиг хүснэгт, цөөн утгатай багана, ховор query-д тавихгүй — хэмжилтээр эхэлж, хэмжилтээр шийднэ.
Дараагийн хичээл бол Түвшин 3-ын төгсгөлийн удирдамжтай дасгал юм. Энэ түвшний бүх хэрэгслийг — JOIN, aggregate, GROUP BY, HAVING, subquery — library2.db дээр номын сангийн таван бодит тайлан бүтээхэд ашиглаж, query бүрийг алхам алхмаар угсарч, монголоор уншина. Эцэст нь таван тайланг reports.sql файл болгон хадгалж Git-д оруулна.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.