Asosiy qismga o'tish

SQL: GROUP BY va agregatlar

9-bob 11 kod bloki 1783 so'z

Bitta raqamdan jadvalgacha

8-bobda COUNT(*) bitta raqam berdi: bekor qilinganlar 1 000 ga yaqin. Analitik savoli esa deyarli har doim "... bo'yicha": to'lov usuli bo'yicha, oy bo'yicha, hudud bo'yicha. Bu GROUP BY. U jadvalni guruhlarga bo'ladi va har guruh uchun bitta qator qaytaradi:

sql3 qator
SELECT status, COUNT(*) AS soni
FROM orders
GROUP BY status
statussoni
cancelled765
delivered18 661
returned574

3 qator, 68 ms

Excel pivot ning Rows va Values qismi aynan shu. Farqi: bu yerda 20 million qator ham bo'lishi mumkin.

Agregat funksiyalar

Funksiya Nima NULL bilan
COUNT(*) qatorlar soni NULL ham sanaladi
COUNT(ustun) bo'sh bo'lmaganlar NULL sanalmaydi
SUM(ustun) yig'indi NULL e'tiborsiz
AVG(ustun) o'rtacha NULL e'tiborsiz (0 deb olmaydi, bu yaxshi)
MIN, MAX eng kichik, eng katta NULL e'tiborsiz
sql10 qator
SELECT
    payment_method,
    COUNT(*)                        AS buyurtmalar,
    SUM(total_amount) / 1000000.0   AS summa_mln,
    ROUND(AVG(total_amount))        AS ortacha_chek,
    MAX(total_amount)               AS eng_katta
FROM orders
WHERE status = 'delivered'
GROUP BY payment_method
ORDER BY summa_mln DESC
payment_methodbuyurtmalarsumma_mlnortacha_chekeng_katta
card5 5899 801.1521 753 65034 391 000
click5 3329 684.2751 816 25643 841 000
payme5 0219 057.4341 803 91028 600 000
cash2 7194 934.0431 814 65435 910 000

4 qator, 34 ms

Bir so'rovda to'rt savolga javob. WHERE guruhlashdan oldin filtrlaydi (faqat yetkazilganlar), ORDER BY natija jadvalini tartiblaydi. ROUND o'rtachadagi kasrni olib tashlaydi: hisobotda 1 816 693.7 emas, 1 816 694.

Qoida: SELECT dagi hamma ustun yo GROUP BY da, yo agregatda

sql3 qator
SELECT payment_method, order_id, COUNT(*)      -- XATO
FROM orders
GROUP BY payment_method

order_id guruhda yuzlab qiymat oladi, qaysi birini ko'rsatsin? PostgreSQL xato beradi. SQLite jimgina tasodifiy bittasini beradi: bu yomonroq, chunki xato ko'rinmaydi. Qoida: SELECT dagi har ustun yo GROUP BY da, yo COUNT/SUM/AVG ichida.

Bir nechta ustun bo'yicha

sql4 qator
SELECT payment_method, status, COUNT(*) AS soni
FROM orders
GROUP BY payment_method, status
ORDER BY payment_method, status
payment_methodstatussoni
cardcancelled184
carddelivered5 589
cardreturned177
cashcancelled273
cashdelivered2 719
cashreturned83
clickcancelled155
clickdelivered5 332
clickreturned163
paymecancelled153
paymedelivered5 021
paymereturned151

12 qator, 27 ms

Har juftlik uchun bitta qator: 4 to'lov usuli × 3 holat = 12 qator. Bu 3-bobdagi long shakl. Pivot dagi Rows + Columns ko'rinishi (wide) uchun 13-bobdagi CASE yoki pandas pivot (19-bob).

Hikoya 5: qaysi to'lovda bekor qilish ko'p

Sonlar o'zi javob bermaydi: naqd to'lovda 300 ta bekor, kartada 200 ta, lekin naqd buyurtmalar kamroq. Ulush kerak:

sql8 qator
SELECT
    payment_method,
    COUNT(*) AS jami,
    SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS bekor,
    ROUND(100.0 * SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) / COUNT(*), 1) AS bekor_foiz
FROM orders
GROUP BY payment_method
ORDER BY bekor_foiz DESC
payment_methodjamibekorbekor_foiz
cash3 0752738.9
card5 9501843.1
payme5 3251532.9
click5 6501552.7

4 qator, 25 ms

Naqd to'lovda bekor qilish ulushi boshqalardan taxminan uch barobar yuqori. Bu 1-bobdagi 5-sir. CASE WHEN ... THEN 1 ELSE 0 END ni SUM ichida ishlatish "shartli sanash" degani, Excel dagi COUNTIF ning SQL varianti. 13-bobda CASE batafsil. 100.0 * dagi nuqta: butun songa bo'lish tuzog'i (7-bob).

Bu jadval qaror uchun yetarli emas, lekin savol uchun yetarli: "naqd buyurtmalarni tasdiqlash uchun qo'ng'iroq qilaylikmi?" Analitik ishi shu: raqamdan savolga.

HAVING: guruhlarni filtrlash

WHERE qatorlarni guruhlashdan oldin filtrlaydi. Guruhlarni natijasiga qarab filtrlash uchun HAVING:

sql7 qator
SELECT customer_id, COUNT(*) AS buyurtmalar, SUM(total_amount) / 1000000.0 AS summa_mln
FROM orders
WHERE status = 'delivered'
GROUP BY customer_id
HAVING COUNT(*) >= 15
ORDER BY buyurtmalar DESC
LIMIT 10
customer_idbuyurtmalarsumma_mln
86761112.529
5985878.761
14549104.248
2644883.081
1 55247125.702
5144590.673
1 3794567.754
2 6574381.325
1 0104277.249
2 7464143.755

10 qator, 104 ms

"15 va undan ko'p buyurtma bergan mijozlar." Bu shartni WHERE ga yozib bo'lmaydi: WHERE COUNT(*) >= 15 xato, chunki WHERE paytida guruh hali yo'q. Ikkalasi bir so'rovda: WHERE qatorlar uchun (yetkazilgan), HAVING guruhlar uchun (15+).

Oy bo'yicha: birinchi vaqt qatori

sql8 qator
SELECT
    substr(order_date, 1, 7)          AS oy,
    COUNT(*)                          AS buyurtmalar,
    SUM(total_amount) / 1000000.0     AS summa_mln
FROM orders
WHERE status = 'delivered'
GROUP BY oy
ORDER BY oy
oybuyurtmalarsumma_mln
2025-017881 324.116
2025-027251 385.47
2025-031 1382 051.271
2025-041 1192 102.522
2025-051 1372 072.246
2025-061 0571 617.648
2025-071 0962 119.16
2025-081 4082 637.128
2025-091 6852 938.99
2025-102 0293 538.945
2025-112 6404 841.466
2025-123 8396 847.942

12 qator, 179 ms

substr(order_date, 1, 7) sananing birinchi 7 belgisi: '2025-06'. ISO format shuning uchun qulay. Bu 1-bobdagi jadval va 4-bobdagi pivot. Uch vosita, bitta javob: iyun 1 618 mln, dekabr 6 848 mln. Agar uchta vositada uch xil raqam chiqsa, bittasida filtr boshqacha: shu yerda status = 'delivered' ekanini eslang.

GROUP BY oy da alias ishlatildi: SQLite va PostgreSQL ikkalasi ham GROUP BY da alias ni qabul qiladi (WHERE dan farqli). Xavfsiz yo'l: ifodani takrorlash, GROUP BY substr(order_date, 1, 7).

Hikoya 3: o'rtacha chek hudud bo'yicha

orders da hudud yo'q, customers da bor. Ularni bog'lash 10-bob (JOIN). Hozircha hudud o'rniga kanal, u ham customers da... demak u ham 10-bobda. GROUP BY ning haqiqiy kuchi JOIN bilan ochiladi: keyingi bob.

Shu bobda mumkin bo'lgani: kategoriya bo'yicha mahsulotlar:

sql10 qator
SELECT
    category,
    COUNT(*)            AS mahsulotlar,
    ROUND(AVG(price))   AS ortacha_narx,
    MIN(price)          AS eng_arzon,
    MAX(price)          AS eng_qimmat,
    SUM(stock)          AS omborda
FROM products
GROUP BY category
ORDER BY ortacha_narx DESC
categorymahsulotlarortacha_narxeng_arzoneng_qimmatomborda
Maishiy texnika101 906 000180 0005 200 0002 448
Elektronika151 772 66735 0007 800 0003 130
Sport9447 22235 0002 400 0001 938
Kiyim12262 50060 000650 0002 291
Uy-ro'zg'or10250 80028 000950 0001 421
Go'zallik8148 75018 000380 0002 027
Oziq-ovqat1080 6006 000160 0001 378
Kitoblar868 75038 000120 0002 029

8 qator, 11 ms

COUNT: yulduzcha, ustun va DISTINCT

Uchta COUNT bor va ular uch xil raqam beradi:

sql6 qator
SELECT
    COUNT(*)                     AS qatorlar,
    COUNT(delivery_days)         AS yetkazish_toldirilgan,
    COUNT(DISTINCT customer_id)  AS nechta_mijoz,
    ROUND(AVG(delivery_days), 2) AS ortacha_kun
FROM orders
qatorlaryetkazish_toldirilgannechta_mijozortacha_kun
20 00019 2352 8193.99

1 qator, 86 ms

COUNT(*) — qatorlar soni. COUNT(ustun) — shu ustunda qiymati bor qatorlar: 20 000 buyurtmadan 19 235 tasida yetkazish kuni bor, qolgan 765 tasida NULL (bekor qilinganlarda yetkazish bo'lmaydi). COUNT(DISTINCT ...) — takrorlanmaydigan qiymatlar: 20 000 buyurtmani 2 819 mijoz bergan.

Shu yerda jim xato yashiringan: AVG(delivery_days) = 3.99 kun — bu 19 235 buyurtmaning o'rtachasi, 20 000 niki emas. Agregatlar NULL ni tashlab ketadi. Agar "har bir buyurtma bo'yicha o'rtacha" kerak bo'lsa, NULL ni ataylab to'ldiring: AVG(COALESCE(delivery_days, 0)) — lekin avval o'ylang, bekor qilingan buyurtmaning yetkazish kuni nol bo'lishi to'g'rimi yoki uni hisobdan chiqarish kerakmi (13-bob).

Shuning uchun "mijozlar soni" degan savolga javob berishdan oldin so'rang: nechta qator yoki nechta odam?

Nisbatlarning o'rtachasini olmang

Har to'lov usuli bo'yicha bekor qilish foizi:

sql7 qator
SELECT
    payment_method,
    COUNT(*) AS buyurtma,
    ROUND(100.0 * SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) / COUNT(*), 2) AS bekor_foiz
FROM orders
GROUP BY payment_method
ORDER BY bekor_foiz DESC
payment_methodbuyurtmabekor_foiz
cash3 0758.88
card5 9503.09
payme5 3252.87
click5 6502.74

4 qator, 192 ms

Endi savol: do'konning umumiy bekor qilish foizi qancha? To'rt foizning o'rtachasini olish oson, lekin noto'g'ri:

sql11 qator
SELECT
    ROUND(AVG(foiz), 2)                  AS foizlar_ortachasi,
    ROUND(100.0 * SUM(bekor) / SUM(n), 2) AS togri_foiz
FROM (
    SELECT
        100.0 * SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) / COUNT(*) AS foiz,
        SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS bekor,
        COUNT(*) AS n
    FROM orders
    GROUP BY payment_method
)
foizlar_ortachasitogri_foiz
4.43.83

1 qator, 210 ms

4.40% va 3.83% — farq sezilarli. Sabab: cash ning bekor foizi eng yuqori (8.88%), lekin u eng kichik guruh (3 075 buyurtma). Oddiy o'rtacha kichik guruhga katta guruh bilan teng vazn beradi.

Qoida: nisbat har doim jami hisob / jami maxraj. Guruhlarning foizini o'rtachalash kerak bo'lsa, og'irlikli o'rtacha oling. Bu xato hisobotlarda juda ko'p uchraydi — 25-bobda yana ko'ramiz.

Xulosa

Tushuncha Asosiy fikr
GROUP BY Har guruh uchun bitta qator. Excel pivot ning bazadagi ko'rinishi
COUNT turlari COUNT(*) qator, COUNT(ustun) NULL siz qator, COUNT(DISTINCT) takrorsiz qiymat
Nisbatlar Foizlarning o'rtachasi ≠ umumiy foiz; jami / jami hisoblang
Agregatlar COUNT, SUM, AVG, MIN, MAX. AVG NULL ni 0 deb olmaydi, tashlab ketadi
Qoida SELECT dagi har ustun yo GROUP BY da, yo agregat ichida. SQLite bu xatoni ko'rsatmaydi
Ko'p ustun GROUP BY a, b → har juftlik uchun qator (long shakl)
Shartli sanash SUM(CASE WHEN ... THEN 1 ELSE 0 END): SQL dagi COUNTIF; ulush uchun 100.0 *
HAVING Guruhlarni natijasiga qarab filtrlash; WHERE qatorlar uchun, HAVING guruhlar uchun
Oy substr(order_date, 1, 7), ISO sana shuning uchun qulay

Talaba ishi

  1. Har status uchun o'rtacha total_amount. Bekor qilinganlar o'rtachasi yetkazilganlardan farq qiladimi? Nega bo'lishi mumkin?
  2. Har kategoriya uchun order_items da sotilgan miqdor (SUM(quantity)) — bu so'rovni yozib ko'ring va nima yetishmayotganini ayting (kategoriya qaysi jadvalda?). 10-bobga tayyorgarlik.
  3. Hafta kunlari bo'yicha buyurtmalar soni: strftime('%w', order_date) 0 (yakshanba) dan 6 gacha beradi. Qaysi kun eng band?
  4. 20 dan ko'p buyurtma bergan mijozlar nechta? (HAVING, keyin tashqi COUNT yoki 11-bobdagi CTE bilan.)

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar