Asosiy qismga o'tish

SQL: ma'lumotni bazadan olish

3-bob 20 kod bloki 2506 so'z

Bu bobdagi kod shu fayllar bilan ishlaydio'z kompyuteringizda ishga tushiring

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.

python9 qator
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())
Chiqish
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?

python6 qator
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 ishlaydi40 mln qatorda
pandasButun jadvalni XOTIRAGA yuklaydi, keyin hisoblaydiXotira yetmaydi yoki kompyuter muzlaydi
SQLHisobni BAZANING O'ZIDA bajaradi, faqat natija keladiBir 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.

python19 qator
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")
Chiqish
Jadvallar: ['ijara', 'tumanlar']
ijara    : 1200 qator
tumanlar : 7 qator

Endi bitta funksiya yozamiz — butun bob shu bilan ishlaydi.

python5 qator
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"))
tumanxonalarmaydon_m2narx_usd
Chilonzor240.6760
Yakkasaroy255.5550
Mirzo Ulug'bek51141 415
Mirzo Ulug'bek372.1985
Chilonzor246.4550

SELECT, WHERE, ORDER BY

SQL ni o'zbek tilida o'qish mumkin. Mana tarjima:

python7 qator
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"},
]))
SQLO'zbekcha
SELECT ustun1, ustun2Shu ustunlarni OLIB BER
FROM jadvalShu jadvaldan
WHERE shartFaqat shu shartga mos qatorlarni
ORDER BY ustun DESCShu ustun bo'yicha kamayish tartibida saralab
LIMIT 10Faqat birinchi 10 tasini

Savol: "Yunusobodda 2 xonali, mebelli kvartiralar — eng qimmati qaysi?"

python9 qator
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
"""))
tumanxonalarmaydon_m2metro_kmnarx_usd
Yunusobod267.13.86970
Yunusobod2521.02935
Yunusobod252.82.22910
Yunusobod256.60.64890
Yunusobod253.41.82885
Yunusobod246.10.39865

Ko'p ishlatiladigan shartlar:

python8 qator
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
"""))
jamiorta_narxchekka_tumanyil_yoq
1 2004402248

`= NULL` hech qachon ishlamaydi

python2 qator
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.

python3 qator
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")
Chiqish
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.

python12 qator
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
"""))
tumanelonlarortacha_narxeng_arzoneng_qimmattez_ketish_foizi
Yunusobod1918123201 38047.6
Yakkasaroy1047972251 29049
Mirzo Ulug'bek1617923151 47052.8
Chilonzor2117833351 38051.2
Shayxontohur1517682351 51550.3
Olmazor1587573051 47051.9
Sergeli1387482501 29052.2
Bektemir867182351 39050

Bitta so'rov — va sizda butun bozor manzarasi bor. Shu jadval 3-bobda grafikka aylanadi.

HAVING — guruhlarni filtrlaydi (WHERE qatorlarni filtrlaydi):

python7 qator
show(sorov("""
    SELECT xonalar, COUNT(*) AS soni, ROUND(AVG(narx_usd)) AS ortacha
    FROM ijara
    GROUP BY xonalar
    HAVING COUNT(*) > 100
    ORDER BY xonalar
"""))
xonalarsoniortacha
1222497
2412680
3345868
41741 046

WHERE va HAVING farqi — klassik intervyu savoli

  • WHERE — guruhlashdan OLDIN ishlaydi, qatorlarni filtrlaydi
  • HAVING — 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.

python12 qator
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
"""))
tumanmetro_liniyaaholi_mingelonlarortacha_narx
YunusobodYunusobod265191812
YakkasaroyO'zbekiston115104797
Mirzo Ulug'bekChilonzor215161792
ChilonzorChilonzor245211783
ShayxontohurO'zbekiston180151768
OlmazorChilonzor290158757
SergeliSergeli195138748

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.

python12 qator
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
"""))
Chiqish
ijara dagi tumanlar  : 8
tumanlar jadvalida   : 7
INNER JOIN dan keyin : 7

Yo'qolgan tuman:
tumanelonlar
Bektemir86

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.

python6 qator
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 turiNimani qoldiradi
INNER JOINFaqat ikkisida ham mos keladigan qatorlar
LEFT JOINCHAPDAGI hamma qator + mos kelgani (mos kelmasa NULL)
RIGHT JOINO'NGDAGI hamma qator (SQLite da yo'q — jadvallarni almashtiring)
FULL OUTER JOINIkkisidagi hamma qator
python10 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
"""))
Chiqish
LEFT JOIN bilan hamma tuman saqlanadi:
tumanmetro_liniyaelonlar
ChilonzorChilonzor211
YunusobodYunusobod191
Mirzo Ulug'bekChilonzor161
OlmazorChilonzor158
ShayxontohurO'zbekiston151
SergeliSergeli138
YakkasaroyO'zbekiston104
Bektemirmetro YO`Q86

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?"

python13 qator
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
"""))
tumanxonalarnarx_usdtuman_ortachasifarqtumandagi_orni
Sergeli51 2907485421
Sergeli41 2207484722
Sergeli51 2107484623
Sergeli31 1657484174
Sergeli41 1607484125
Sergeli41 1357483876
Sergeli41 1307483827
Sergeli41 1257483778
Sergeli41 0907483429
Sergeli41 06574831710

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

python12 qator
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"},
]))
VazifaQaysi biriNega
40 mln qatordan 10 ming tasini olishSQLXotiraga hammasi sig'maydi
Guruhlab yig'ish, JOINSQLBaza buni indeks bilan tez bajaradi
Grafik chizishpandasSQL grafik chizmaydi
Model o'qitishpandassklearn DataFrame/massiv kutadi
Murakkab tozalash, applypandasPython funksiyalarini ishlatish mumkin

Amaliy naqsh: SQL bilan kamaytirib olasiz, pandas bilan ishlaysiz.

python12 qator
# 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))
Chiqish
Bazadan keldi: 1199 qator, 7 ustun
xonalarmaydon_m2metro_kmmebeltumanaholi_mingijaraga_berildi_30k
240.61.431Chilonzor2450
255.54.181Yakkasaroy1150
51143.841Mirzo Ulug'bek2150
372.12.991Mirzo Ulug'bek2150
246.44.580Chilonzor2451

Shu DataFrame endi 0-bobdagi protokolning 2-qadamiga tayyor.

Bugun nima o'rgandik

python9 qator
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"},
]))
BuyruqNima qiladi
SELECT / FROM / WHEREUstun va qator tanlash
IS NULLBo'sh qiymat tekshirish (= NULL ishlamaydi)
GROUP BY + AVG/COUNTGuruhlab hisoblash
HAVINGGuruhlarni filtrlash (WHERE qatorlarni)
INNER JOINMos kelganlarni bog'laydi — qator YO'QOTADI
LEFT JOINChapdagi hammasini saqlaydi
OVER (PARTITION BY)Qatorni saqlab, guruh hisobini qo'shadi

JOIN dan keyin har doim shu ikki qatorni yozing

python2 qator
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.

Telegramda yozish Narxlar