SQL: ma'lumotni bazadan olish
Sahna: CSV yo'q
Siz 0-bobdagi protokolni o'rgandingiz. Birinchi qadam — pd.read_csv(...).
Keyin ishga kirasiz va direktor aytadi: "Ma'lumot bazada. Mana ulanish ma'lumotlari."
CSV yo'q. Hech kim sizga fayl bermaydi. Ma'lumot ma'lumotlar bazasida — va u 40 million qator, uni butunlay yuklash mumkin emas.
Bu bob shuni hal qiladi. Bu — protokolning 1-qadami, lekin haqiqiy sharoitda.
Nega pandas yetarli emas
Savol beraylik: "har bir tumanda o'rtacha narx qancha?"
Ikki yo'l bor.
import pandas as pd import sqlite3 df = pd.read_csv(DATA / "uy_ijara.csv") # 1-yo'l: pandas — avval HAMMASINI xotiraga yuklaymiz natija_pandas = df.groupby("tuman").narx_usd.mean().round(1).sort_values(ascending=False) print("pandas:") print(natija_pandas.to_string())
pandas: tuman Yunusobod 811.8 Yakkasaroy 797.4 Mirzo Ulug'bek 792.4 Chilonzor 783.5 Shayxontohur 767.8 Olmazor 756.8 Sergeli 747.6 Bektemir 718.4
Bu ishladi, chunki bizda 1200 qator. Lekin 40 million qator bo'lsa?
show(pd.DataFrame([ {"": "pandas", "Qanday ishlaydi": "Butun jadvalni XOTIRAGA yuklaydi, keyin hisoblaydi", "40 mln qatorda": "Xotira yetmaydi yoki kompyuter muzlaydi"}, {"": "SQL", "Qanday ishlaydi": "Hisobni BAZANING O'ZIDA bajaradi, faqat natija keladi", "40 mln qatorda": "Bir necha sekund, 8 qator natija qaytadi"}, ]))
| Qanday ishlaydi | 40 mln qatorda | |
|---|---|---|
| pandas | Butun jadvalni XOTIRAGA yuklaydi, keyin hisoblaydi | Xotira yetmaydi yoki kompyuter muzlaydi |
| SQL | Hisobni BAZANING O'ZIDA bajaradi, faqat natija keladi | Bir necha sekund, 8 qator natija qaytadi |
Qoida oddiy: ma'lumot katta bo'lsa, hisobni ma'lumot turgan joyda bajaring. Xotiraga faqat natijani olib kelasiz.
Baza bilan ishlash: uch qator kod
Python da sqlite3 ichida bor — o'rnatish kerak emas. Keling, o'zimiz kichik baza yasaymiz.
baza = sqlite3.connect(":memory:") # xotirada baza (fayl kerak emas) df.to_sql("ijara", baza, index=False, if_exists="replace") # ikkinchi jadval — JOIN uchun kerak bo'ladi tumanlar = pd.DataFrame([ {"tuman": "Yunusobod", "metro_liniya": "Yunusobod", "aholi_ming": 265}, {"tuman": "Chilonzor", "metro_liniya": "Chilonzor", "aholi_ming": 245}, {"tuman": "Mirzo Ulug'bek", "metro_liniya": "Chilonzor", "aholi_ming": 215}, {"tuman": "Shayxontohur", "metro_liniya": "O'zbekiston", "aholi_ming": 180}, {"tuman": "Yakkasaroy", "metro_liniya": "O'zbekiston", "aholi_ming": 115}, {"tuman": "Olmazor", "metro_liniya": "Chilonzor", "aholi_ming": 290}, {"tuman": "Sergeli", "metro_liniya": "Sergeli", "aholi_ming": 195}, # DIQQAT: Bektemir YO'Q — LEFT JOIN darsida kerak bo'ladi ]) tumanlar.to_sql("tumanlar", baza, index=False, if_exists="replace") print("Jadvallar:", pd.read_sql("SELECT name FROM sqlite_master WHERE type='table'", baza).name.tolist()) print(f"ijara : {pd.read_sql('SELECT COUNT(*) n FROM ijara', baza).n[0]} qator") print(f"tumanlar : {pd.read_sql('SELECT COUNT(*) n FROM tumanlar', baza).n[0]} qator")
Jadvallar: ['ijara', 'tumanlar'] ijara : 1200 qator tumanlar : 7 qator
Endi bitta funksiya yozamiz — butun bob shu bilan ishlaydi.
def sorov(sql): """SQL ni bajaradi va natijani DataFrame qilib qaytaradi.""" return pd.read_sql(sql, baza) show(sorov("SELECT tuman, xonalar, maydon_m2, narx_usd FROM ijara LIMIT 5"))
| tuman | xonalar | maydon_m2 | narx_usd |
|---|---|---|---|
| Chilonzor | 2 | 40.6 | 760 |
| Yakkasaroy | 2 | 55.5 | 550 |
| Mirzo Ulug'bek | 5 | 114 | 1 415 |
| Mirzo Ulug'bek | 3 | 72.1 | 985 |
| Chilonzor | 2 | 46.4 | 550 |
SELECT, WHERE, ORDER BY
SQL ni o'zbek tilida o'qish mumkin. Mana tarjima:
show(pd.DataFrame([ {"SQL": "SELECT ustun1, ustun2", "O'zbekcha": "Shu ustunlarni OLIB BER"}, {"SQL": "FROM jadval", "O'zbekcha": "Shu jadvaldan"}, {"SQL": "WHERE shart", "O'zbekcha": "Faqat shu shartga mos qatorlarni"}, {"SQL": "ORDER BY ustun DESC", "O'zbekcha": "Shu ustun bo'yicha kamayish tartibida saralab"}, {"SQL": "LIMIT 10", "O'zbekcha": "Faqat birinchi 10 tasini"}, ]))
| SQL | O'zbekcha |
|---|---|
| SELECT ustun1, ustun2 | Shu ustunlarni OLIB BER |
| FROM jadval | Shu jadvaldan |
| WHERE shart | Faqat shu shartga mos qatorlarni |
| ORDER BY ustun DESC | Shu ustun bo'yicha kamayish tartibida saralab |
| LIMIT 10 | Faqat birinchi 10 tasini |
Savol: "Yunusobodda 2 xonali, mebelli kvartiralar — eng qimmati qaysi?"
show(sorov(""" SELECT tuman, xonalar, maydon_m2, metro_km, narx_usd FROM ijara WHERE tuman = 'Yunusobod' AND xonalar = 2 AND mebel = 1 ORDER BY narx_usd DESC LIMIT 6 """))
| tuman | xonalar | maydon_m2 | metro_km | narx_usd |
|---|---|---|---|---|
| Yunusobod | 2 | 67.1 | 3.86 | 970 |
| Yunusobod | 2 | 52 | 1.02 | 935 |
| Yunusobod | 2 | 52.8 | 2.22 | 910 |
| Yunusobod | 2 | 56.6 | 0.64 | 890 |
| Yunusobod | 2 | 53.4 | 1.82 | 885 |
| Yunusobod | 2 | 46.1 | 0.39 | 865 |
Ko'p ishlatiladigan shartlar:
show(sorov(""" SELECT COUNT(*) AS jami, SUM(CASE WHEN narx_usd BETWEEN 400 AND 700 THEN 1 ELSE 0 END) AS orta_narx, SUM(CASE WHEN tuman IN ('Sergeli', 'Bektemir') THEN 1 ELSE 0 END) AS chekka_tuman, SUM(CASE WHEN qurilgan_yil IS NULL THEN 1 ELSE 0 END) AS yil_yoq FROM ijara """))
| jami | orta_narx | chekka_tuman | yil_yoq |
|---|---|---|---|
| 1 200 | 440 | 224 | 8 |
`= NULL` hech qachon ishlamaydi
WHERE qurilgan_yil = NULL -- HAR DOIM bo'sh qaytaradi WHERE qurilgan_yil IS NULL -- TO'G'RI
NULL — "qiymat yo'q" degani, va u hech narsaga, hatto o'ziga ham teng emas. Bu SQL dagi eng ko'p uchraydigan xato.
print("Isbot:") print(" = NULL natijasi:", sorov("SELECT COUNT(*) n FROM ijara WHERE qurilgan_yil = NULL").n[0], "qator") print(" IS NULL natijasi:", sorov("SELECT COUNT(*) n FROM ijara WHERE qurilgan_yil IS NULL").n[0], "qator")
Isbot: = NULL natijasi: 0 qator IS NULL natijasi: 8 qator
GROUP BY — guruhlab hisoblash
Bu — analitikaning asosiy quroli. "Har bir X uchun Y qancha" degan savollarga javob beradi.
show(sorov(""" SELECT tuman, COUNT(*) AS elonlar, ROUND(AVG(narx_usd)) AS ortacha_narx, MIN(narx_usd) AS eng_arzon, MAX(narx_usd) AS eng_qimmat, ROUND(AVG(ijaraga_berildi_30k) * 100, 1) AS tez_ketish_foizi FROM ijara GROUP BY tuman ORDER BY ortacha_narx DESC """))
| tuman | elonlar | ortacha_narx | eng_arzon | eng_qimmat | tez_ketish_foizi |
|---|---|---|---|---|---|
| Yunusobod | 191 | 812 | 320 | 1 380 | 47.6 |
| Yakkasaroy | 104 | 797 | 225 | 1 290 | 49 |
| Mirzo Ulug'bek | 161 | 792 | 315 | 1 470 | 52.8 |
| Chilonzor | 211 | 783 | 335 | 1 380 | 51.2 |
| Shayxontohur | 151 | 768 | 235 | 1 515 | 50.3 |
| Olmazor | 158 | 757 | 305 | 1 470 | 51.9 |
| Sergeli | 138 | 748 | 250 | 1 290 | 52.2 |
| Bektemir | 86 | 718 | 235 | 1 390 | 50 |
Bitta so'rov — va sizda butun bozor manzarasi bor. Shu jadval 3-bobda grafikka aylanadi.
HAVING — guruhlarni filtrlaydi (WHERE qatorlarni filtrlaydi):
show(sorov(""" SELECT xonalar, COUNT(*) AS soni, ROUND(AVG(narx_usd)) AS ortacha FROM ijara GROUP BY xonalar HAVING COUNT(*) > 100 ORDER BY xonalar """))
| xonalar | soni | ortacha |
|---|---|---|
| 1 | 222 | 497 |
| 2 | 412 | 680 |
| 3 | 345 | 868 |
| 4 | 174 | 1 046 |
WHERE va HAVING farqi — klassik intervyu savoli
WHERE— guruhlashdan OLDIN ishlaydi, qatorlarni filtrlaydiHAVING— guruhlashdan KEYIN ishlaydi, guruhlarni filtrlaydi
WHERE COUNT(*) > 100 yozsangiz xato beradi: guruhlash hali bo'lmagan, COUNT mavjud emas.
JOIN — jadvallarni bog'lash
Haqiqiy bazada ma'lumot bir jadvalda bo'lmaydi. Kvartiralar bir jadvalda, tumanlar haqida ma'lumot boshqasida.
show(sorov(""" SELECT i.tuman, t.metro_liniya, t.aholi_ming, COUNT(*) AS elonlar, ROUND(AVG(i.narx_usd)) AS ortacha_narx FROM ijara i INNER JOIN tumanlar t ON i.tuman = t.tuman GROUP BY i.tuman, t.metro_liniya, t.aholi_ming ORDER BY ortacha_narx DESC """))
| tuman | metro_liniya | aholi_ming | elonlar | ortacha_narx |
|---|---|---|---|---|
| Yunusobod | Yunusobod | 265 | 191 | 812 |
| Yakkasaroy | O'zbekiston | 115 | 104 | 797 |
| Mirzo Ulug'bek | Chilonzor | 215 | 161 | 792 |
| Chilonzor | Chilonzor | 245 | 211 | 783 |
| Shayxontohur | O'zbekiston | 180 | 151 | 768 |
| Olmazor | Chilonzor | 290 | 158 | 757 |
| Sergeli | Sergeli | 195 | 138 | 748 |
Diqqat: natijada 7 ta tuman bor. Lekin ma'lumotda 8 ta tuman edi.
Bektemir yo'qoldi — chunki u tumanlar jadvalida yo'q, va INNER JOIN faqat ikkisida ham bor qatorlarni qoldiradi.
print("ijara dagi tumanlar :", sorov("SELECT COUNT(DISTINCT tuman) n FROM ijara").n[0]) print("tumanlar jadvalida :", sorov("SELECT COUNT(*) n FROM tumanlar").n[0]) print("INNER JOIN dan keyin :", len(sorov("SELECT DISTINCT i.tuman FROM ijara i INNER JOIN tumanlar t ON i.tuman=t.tuman"))) print() print("Yo'qolgan tuman:") show(sorov(""" SELECT i.tuman, COUNT(*) AS elonlar FROM ijara i LEFT JOIN tumanlar t ON i.tuman = t.tuman WHERE t.tuman IS NULL GROUP BY i.tuman """))
ijara dagi tumanlar : 8 tumanlar jadvalida : 7 INNER JOIN dan keyin : 7 Yo'qolgan tuman:
| tuman | elonlar |
|---|---|
| Bektemir | 86 |
INNER JOIN ma'lumotni JIM yo'qotadi
Bu — ML loyihalarida eng qimmat SQL xatosi. Siz INNER JOIN yozasiz, so'rov ishlaydi, xato chiqmaydi — va datasetingizdan 93 qator yo'qoladi.
Keyin model o'rgatasiz va Bektemir tumanini hech qachon ko'rmaydi.
Qoida: JOIN dan keyin har doim qator sonini tekshiring. Kamaygan bo'lsa — LEFT JOIN ga o'ting va nega kamayganini bilib oling.
show(pd.DataFrame([ {"JOIN turi": "INNER JOIN", "Nimani qoldiradi": "Faqat ikkisida ham mos keladigan qatorlar"}, {"JOIN turi": "LEFT JOIN", "Nimani qoldiradi": "CHAPDAGI hamma qator + mos kelgani (mos kelmasa NULL)"}, {"JOIN turi": "RIGHT JOIN", "Nimani qoldiradi": "O'NGDAGI hamma qator (SQLite da yo'q — jadvallarni almashtiring)"}, {"JOIN turi": "FULL OUTER JOIN", "Nimani qoldiradi": "Ikkisidagi hamma qator"}, ]))
| JOIN turi | Nimani qoldiradi |
|---|---|
| INNER JOIN | Faqat ikkisida ham mos keladigan qatorlar |
| LEFT JOIN | CHAPDAGI hamma qator + mos kelgani (mos kelmasa NULL) |
| RIGHT JOIN | O'NGDAGI hamma qator (SQLite da yo'q — jadvallarni almashtiring) |
| FULL OUTER JOIN | Ikkisidagi hamma qator |
print("LEFT JOIN bilan hamma tuman saqlanadi:") show(sorov(""" SELECT i.tuman, COALESCE(t.metro_liniya, 'metro YO`Q') AS metro_liniya, COUNT(*) AS elonlar FROM ijara i LEFT JOIN tumanlar t ON i.tuman = t.tuman GROUP BY i.tuman, t.metro_liniya ORDER BY elonlar DESC """))
LEFT JOIN bilan hamma tuman saqlanadi:
| tuman | metro_liniya | elonlar |
|---|---|---|
| Chilonzor | Chilonzor | 211 |
| Yunusobod | Yunusobod | 191 |
| Mirzo Ulug'bek | Chilonzor | 161 |
| Olmazor | Chilonzor | 158 |
| Shayxontohur | O'zbekiston | 151 |
| Sergeli | Sergeli | 138 |
| Yakkasaroy | O'zbekiston | 104 |
| Bektemir | metro YO`Q | 86 |
Window function — guruhni buzmasdan hisoblash
GROUP BY qatorlarni yig'ib tashlaydi. Ba'zan esa har qatorni saqlab, yoniga guruh bo'yicha hisobni qo'yish kerak.
Savol: "Har bir kvartira o'z tumanidagi o'rtachadan qimmatmi yoki arzonmi?"
show(sorov(""" SELECT tuman, xonalar, narx_usd, ROUND(AVG(narx_usd) OVER (PARTITION BY tuman)) AS tuman_ortachasi, narx_usd - ROUND(AVG(narx_usd) OVER (PARTITION BY tuman)) AS farq, RANK() OVER (PARTITION BY tuman ORDER BY narx_usd DESC) AS tumandagi_orni FROM ijara WHERE tuman IN ('Yunusobod', 'Sergeli') ORDER BY tuman, tumandagi_orni LIMIT 10 """))
| tuman | xonalar | narx_usd | tuman_ortachasi | farq | tumandagi_orni |
|---|---|---|---|---|---|
| Sergeli | 5 | 1 290 | 748 | 542 | 1 |
| Sergeli | 4 | 1 220 | 748 | 472 | 2 |
| Sergeli | 5 | 1 210 | 748 | 462 | 3 |
| Sergeli | 3 | 1 165 | 748 | 417 | 4 |
| Sergeli | 4 | 1 160 | 748 | 412 | 5 |
| Sergeli | 4 | 1 135 | 748 | 387 | 6 |
| Sergeli | 4 | 1 130 | 748 | 382 | 7 |
| Sergeli | 4 | 1 125 | 748 | 377 | 8 |
| Sergeli | 4 | 1 090 | 748 | 342 | 9 |
| Sergeli | 4 | 1 065 | 748 | 317 | 10 |
Qatorlar yo'qolmadi — har biri o'z joyida, lekin yoniga guruh hisobi qo'shildi. GROUP BY bilan buni qilish mumkin emas.
Bu ML uchun juda foydali: shunday ustunlar yaxshi xususiyat bo'ladi (22-bob).
SQL va pandas: qachon qaysi
show(pd.DataFrame([ {"Vazifa": "40 mln qatordan 10 ming tasini olish", "Qaysi biri": "SQL", "Nega": "Xotiraga hammasi sig'maydi"}, {"Vazifa": "Guruhlab yig'ish, JOIN", "Qaysi biri": "SQL", "Nega": "Baza buni indeks bilan tez bajaradi"}, {"Vazifa": "Grafik chizish", "Qaysi biri": "pandas", "Nega": "SQL grafik chizmaydi"}, {"Vazifa": "Model o'qitish", "Qaysi biri": "pandas", "Nega": "sklearn DataFrame/massiv kutadi"}, {"Vazifa": "Murakkab tozalash, apply", "Qaysi biri": "pandas", "Nega": "Python funksiyalarini ishlatish mumkin"}, ]))
| Vazifa | Qaysi biri | Nega |
|---|---|---|
| 40 mln qatordan 10 ming tasini olish | SQL | Xotiraga hammasi sig'maydi |
| Guruhlab yig'ish, JOIN | SQL | Baza buni indeks bilan tez bajaradi |
| Grafik chizish | pandas | SQL grafik chizmaydi |
| Model o'qitish | pandas | sklearn DataFrame/massiv kutadi |
| Murakkab tozalash, apply | pandas | Python funksiyalarini ishlatish mumkin |
Amaliy naqsh: SQL bilan kamaytirib olasiz, pandas bilan ishlaysiz.
# Bazadan FAQAT kerakli qismni olamiz — bu haqiqiy loyihadagi naqsh model_uchun = sorov(""" SELECT i.xonalar, i.maydon_m2, i.metro_km, i.mebel, i.tuman, COALESCE(t.aholi_ming, 0) AS aholi_ming, i.ijaraga_berildi_30k FROM ijara i LEFT JOIN tumanlar t ON i.tuman = t.tuman WHERE i.narx_usd BETWEEN 200 AND 1500 """) print(f"Bazadan keldi: {model_uchun.shape[0]} qator, {model_uchun.shape[1]} ustun") print() show(model_uchun.head(5))
Bazadan keldi: 1199 qator, 7 ustun
| xonalar | maydon_m2 | metro_km | mebel | tuman | aholi_ming | ijaraga_berildi_30k |
|---|---|---|---|---|---|---|
| 2 | 40.6 | 1.43 | 1 | Chilonzor | 245 | 0 |
| 2 | 55.5 | 4.18 | 1 | Yakkasaroy | 115 | 0 |
| 5 | 114 | 3.84 | 1 | Mirzo Ulug'bek | 215 | 0 |
| 3 | 72.1 | 2.99 | 1 | Mirzo Ulug'bek | 215 | 0 |
| 2 | 46.4 | 4.58 | 0 | Chilonzor | 245 | 1 |
Shu DataFrame endi 0-bobdagi protokolning 2-qadamiga tayyor.
Bugun nima o'rgandik
show(pd.DataFrame([ {"Buyruq": "SELECT / FROM / WHERE", "Nima qiladi": "Ustun va qator tanlash"}, {"Buyruq": "IS NULL", "Nima qiladi": "Bo'sh qiymat tekshirish (= NULL ishlamaydi)"}, {"Buyruq": "GROUP BY + AVG/COUNT", "Nima qiladi": "Guruhlab hisoblash"}, {"Buyruq": "HAVING", "Nima qiladi": "Guruhlarni filtrlash (WHERE qatorlarni)"}, {"Buyruq": "INNER JOIN", "Nima qiladi": "Mos kelganlarni bog'laydi — qator YO'QOTADI"}, {"Buyruq": "LEFT JOIN", "Nima qiladi": "Chapdagi hammasini saqlaydi"}, {"Buyruq": "OVER (PARTITION BY)", "Nima qiladi": "Qatorni saqlab, guruh hisobini qo'shadi"}, ]))
| Buyruq | Nima qiladi |
|---|---|
| SELECT / FROM / WHERE | Ustun va qator tanlash |
| IS NULL | Bo'sh qiymat tekshirish (= NULL ishlamaydi) |
| GROUP BY + AVG/COUNT | Guruhlab hisoblash |
| HAVING | Guruhlarni filtrlash (WHERE qatorlarni) |
| INNER JOIN | Mos kelganlarni bog'laydi — qator YO'QOTADI |
| LEFT JOIN | Chapdagi hammasini saqlaydi |
| OVER (PARTITION BY) | Qatorni saqlab, guruh hisobini qo'shadi |
JOIN dan keyin har doim shu ikki qatorni yozing
print(len(oldin), "->", len(keyin)) assert len(keyin) == len(oldin), "JOIN qator yo'qotdi!"
Bu ikki qator sizni eng jim ma'lumot yo'qotishidan saqlaydi.
Keyingi bobda: ma'lumot keldi, lekin 1200 qatorni ko'z bilan o'qib bo'lmaydi. Uni ko'ramiz.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.