11 β Aggregate funksiyalar va GROUP BY¶
β¬ οΈ Oldingi: 10 β Built-in funksiyalar (matn, son, sana) Β· π README Β· Keyingi: 12 β JOIN β jadvallarni bog'lash β‘οΈ
Bu bobda: ko'p qatordan bitta xulosa chiqaradigan aggregate funksiyalarni (
COUNT,SUM,AVG,MIN,MAX) va "har bir ... bo'yicha" savollariga javob beradiganGROUP BY'ni o'rganamiz. Yo'l-yo'lakayCOUNT(*)vaCOUNT(ustun)orasidagi NULL farqini, tayyor guruhlarni filtrlaydiganHAVING'ni va query'ning bajarilish tartibini ko'rib chiqamiz.
Aggregate β ko'p qatordan BITTA natija¶
Shu paytgacha yozgan query'larimiz qatorlarni ro'yxat qilib qaytarardi. Endi boshqa turdagi savollarga o'tamiz: "jami nechta?", "o'rtacha qancha?", "eng kattasi qaysi?". Bunday savollarga ro'yxat emas, bitta qiymat javob bo'ladi.
Aggregate funksiya β ko'p qatorni "presslab" bitta qiymat chiqaradigan mashina. Sinf jurnalini eslang: jurnalda 30 ta baho turibdi, lekin "sinfning o'rtacha bahosi" β bitta son. Beshta asosiy aggregate bor:
| Funksiya | Nima qiladi | Qanday savolga javob |
|---|---|---|
COUNT(*) |
qatorlarni sanaydi | "jami nechta?" |
SUM(ustun) |
yig'indini hisoblaydi | "hammasi qo'shilsa qancha?" |
AVG(ustun) |
o'rtachani topadi | "o'rtacha qancha?" |
MIN(ustun) |
eng kichigini topadi | "eng kichigi/arzoni/eskisi?" |
MAX(ustun) |
eng kattasini topadi | "eng kattasi/qimmati/yangisi?" |
USE dokon;
SELECT COUNT(*) FROM mahsulotlar; -- nechta mahsulot bor: 12
SELECT SUM(soni) FROM mahsulotlar; -- omborda jami nechta dona: 361
SELECT AVG(narx) FROM mahsulotlar; -- o'rtacha narx: ~5 235 833
SELECT MIN(narx), MAX(narx) FROM mahsulotlar; -- eng arzon (50 000) va eng qimmat (16 000 000)
Bir nechta aggregate'ni bitta query'da yozish mumkin β alias bilan natija chiroyli o'qiladi:
SELECT COUNT(*) AS mahsulotlar_soni,
MIN(narx) AS eng_arzon,
MAX(narx) AS eng_qimmat
FROM mahsulotlar;
β οΈ Eng ko'p uchraydigan boshlovchi xatosi β aggregate yoniga oddiy ustun qo'shib yuborish:
"Eng qimmat mahsulotning nomi chiqadi" deb o'ylash tabiiy, lekin MySQL uchun bu ikkita qarama-qarshi topshiriq: MAX 12 qatordan bitta son yasaydi, nomi esa 12 ta. Qaysi nomni yozsin? MySQL 8 bunga to'g'ridan-to'g'ri xato beradi. "Eng qimmati qaysi?" degan savolga to'g'ri yo'l β 9-bobdagi tanish qolip:
COUNT(*) vs COUNT(ustun) β NULL tuzog'i¶
Muhim nozik joy:
SELECT COUNT(*) FROM kutubxona.azolar; -- 6 β HAMMA qator
SELECT COUNT(telefon) FROM kutubxona.azolar; -- 5 β kamroq! NULL'lar SANALMAYDI
COUNT(*) qatorlarning o'zini sanaydi, COUNT(ustun) esa shu ustundagi NULL bo'lmagan qiymatlarni. Nilufarning telefoni yozilmagan (NULL) β shuning uchun u ikkinchi sanoqqa kirmadi. (6-bobdagi mashqlarda o'zingiz qator qo'shgan bo'lsangiz, sonlaringiz kattaroq chiqadi β farqning o'zi baribir ko'rinadi.)
Xuddi shu qoida SUM va AVG'ga ham tegishli: ular NULL'larni shunchaki e'tiborsiz qoldiradi. Masalan, AVG(baho) β baho qo'yilgan safarlarninggina o'rtachasi; NULL'lar yig'indiga ham, bo'luvchiga ham kirmaydi. Bu ko'pincha aynan biz xohlagan narsa, lekin bilib turish kerak.
π‘ Yana bir foydali shakl β COUNT(DISTINCT ustun): nechta har xil qiymat borligini sanaydi:
GROUP BY β "har bir ... bo'yicha"¶
Aggregate butun jadvalga bitta natija beradi. Savol "har bir ... bo'yicha" bo'lsa-chi? Mana shu yerda GROUP BY sahnaga chiqadi. Savolda "har bir" so'zini eshitsangiz β bu GROUP BY:
- "Har bir janrda nechta kitob?" β
GROUP BY janr - "Har bir shahardan nechta mijoz?" β
GROUP BY shahar - "Har bir haydovchi qancha topdi?" β
GROUP BY haydovchi_id
USE kutubxona;
SELECT janr, COUNT(*) AS soni
FROM kitoblar
GROUP BY janr;
-- janr | soni
-- roman | 4
-- sarguzasht | 3
-- sheriyat | 2
-- ertak | 1
Qanday ishlaydi: MySQL qatorlarni janr qiymati bo'yicha "to'plarga" ajratadi (roman to'pi, sarguzasht to'pi...), keyin har to'pga alohida COUNT/SUM/AVG qo'llaydi. Nechta to'p bo'lsa β natijada shuncha qator.
π MySQL 8'da GROUP BY natijani saralamaydi (eski versiyalarda saralanardi, 8.0 dan bu olib tashlangan) β guruhlar istalgan tartibda chiqishi mumkin. Tartib kerak bo'lsa, oxiriga ORDER BY qo'shing.
Har guruhga bir nechta aggregate'ni birdan qo'llash mumkin:
SELECT janr,
COUNT(*) AS soni,
ROUND(AVG(sahifa)) AS ortacha_sahifa
FROM kitoblar
GROUP BY janr;
-- janr | soni | ortacha_sahifa
-- roman | 4 | 725
-- sarguzasht | 3 | 288
-- sheriyat | 2 | 160
-- ertak | 1 | 96
Bir necha ustun bo'yicha guruhlash ham bo'ladi β bunda har bir kombinatsiya alohida to'p:
-- Har mijozning har holatdagi buyurtmalari soni:
SELECT mijoz_id, holat, COUNT(*) AS soni
FROM dokon.buyurtmalar
GROUP BY mijoz_id, holat;
-- mijoz_id | holat | soni
-- 1 | yetkazildi | 2
-- 1 | yangi | 1
-- 2 | yetkazildi | 1
-- ...
Oltin qoida (ONLY_FULL_GROUP_BY)¶
SELECT'da faqat ikki narsa turishi mumkin: GROUP BY'dagi ustun yoki aggregate funksiya. Boshqa ustun yozsangiz:
SELECT janr, nomi, COUNT(*) FROM kitoblar GROUP BY janr;
-- β Error 1055: ...'kutubxona.kitoblar.nomi' is not in GROUP BY clause...
Nega? roman to'pida 4 ta kitob bor β nomi ustuniga MySQL qaysi birini yozsin? To'g'ri javob yo'q. MySQL 8'da ONLY_FULL_GROUP_BY rejimi default yoqilgan va bunday query darhol xato bilan to'xtaydi. Eski versiyalarda bu rejim o'chiq edi: MySQL to'pdan duch kelgan bitta nomni qaytarardi va xato sezilmasdan noto'g'ri hisobotlar tarqalaverardi. Shuning uchun bu xato β dushman emas, qo'riqchi.
HAVING β guruhlarni filtrlash¶
"2 tadan ko'p kitobi bor janrlar" β bu yerda alohida kitoblarni emas, tayyor to'plarning o'zini filtrlash kerak. Buning uchun HAVING bor:
SELECT janr, COUNT(*) AS soni
FROM kitoblar
GROUP BY janr
HAVING soni > 2;
-- roman | 4
-- sarguzasht | 3
WHERE vs HAVING:
WHERE |
HAVING |
|
|---|---|---|
| Nimani filtrlaydi | alohida qatorlarni | tayyor guruhlarni |
| Qachon ishlaydi | guruhlashdan oldin | guruhlashdan keyin |
| Aggregate (COUNT, SUM...) | β ishlatib bo'lmaydi | β bemalol |
Ikkalasi bitta query'da bemalol birga keladi β har biri o'z ishini qiladi:
-- 1950-yildan keyingi kitoblar ichida, 1 tadan ko'p kitobli janrlar:
SELECT janr, COUNT(*) AS soni
FROM kitoblar
WHERE yil > 1950 -- avval qatorlar filtrlanadi
GROUP BY janr -- qolganlari to'plarga ajratiladi
HAVING soni > 1; -- keyin to'plar filtrlanadi
-- sarguzasht | 2
-- sheriyat | 2
π‘ HAVING soni > 2 dagi soni β SELECT'dagi alias. Alias'ni HAVING'da ishlatish β MySQL'ning qulayligi (standart SQL'da HAVING COUNT(*) > 2 deb yoziladi; MySQL'da ikkalasi ham ishlaydi). WHERE'da esa alias ISHLAMAYDI β sababi quyida.
Guruhlarni saralash β TOP-N to'plarga ham ishlaydi¶
GROUP BY natijasi ham oddiy natija β unga 9-bobdagi ORDER BY + LIMIT qolipini qo'llash mumkin:
-- Eng ko'p kitobli janr:
SELECT janr, COUNT(*) AS soni
FROM kitoblar
GROUP BY janr
ORDER BY soni DESC
LIMIT 1; -- roman | 4
"Eng sersafar haydovchi", "eng sertushum kun", "eng ko'p buyurtma qilgan mijoz" β hammasi shu qolip: avval guruhlab sanaymiz, keyin saralab tepadagisini olamiz.
Query bajarilish tartibi (devorga yopishtiring!)¶
Siz query'ni SELECT'dan boshlab yozasiz, lekin MySQL uni FROM'dan boshlab bajaradi. Shu tartibdan ikkita amaliy xulosa kelib chiqadi:
WHERE COUNT(*) > 2β β WHERE ishlayotgan paytda hali to'plar YO'Q, demak aggregate ham yo'q. Guruh sharti β faqat HAVING'da.WHERE soni > 2β β alias SELECT'da tug'iladi, SELECT esa WHERE'dan ancha keyin keladi. (HAVING'da alias ishlashi β MySQL'ning maxsus qulayligi. ORDER BY esa tartibda SELECT'dan keyin turadi, shuning uchun unda alias tabiiy ishlaydi.)
11-bob masalalari¶
- (kutubxona) Jami nechta kitob bor?
- (kutubxona) Barcha kitob nusxalarining yig'indisi (
SUM(nusxa_soni)) - (kutubxona) O'rtacha sahifa soni (
ROUNDbilan yaxlitlang) - (kutubxona) Eng qadimgi va eng yangi kitob yili (
MIN,MAXβ bitta query'da) - (kutubxona) Telefoni bor a'zolar soni (
COUNT(telefon)β NULL'lar sanalmasligini tekshiring!) - (kutubxona) Har janrda nechta kitob?
- (kutubxona) Har janrning o'rtacha sahifa soni
- (kutubxona) Har muallif (
muallif_id) nechta kitob yozgan? - (kutubxona) 2 tadan ko'p kitob yozgan mualliflar (
HAVING) - (kutubxona) Har a'zo nechta marta kitob olgan? (
ijaralarjadvali,GROUP BY azo_id) - (dokon) Har kategoriyada nechta mahsulot bor va o'rtacha narxi qancha?
- (dokon) Har kategoriyaning ombor qiymati:
SUM(narx * soni) - (dokon) Har shahardan nechta mijoz?
- (dokon) Har mijoz nechta buyurtma qilgan? (
GROUP BY mijoz_id) - (dokon) Har buyurtmaning umumiy summasi:
buyurtma_qatorlaridaGROUP BY buyurtma_id,SUM(narx * soni) - (dokon) Holatlar bo'yicha buyurtmalar soni (
GROUP BY holat) - (klinika) Har shifokor nechta qabul qilgan va jami qancha to'lov yig'gan? (
COUNT+SUM(tolov)) - (klinika) Har bemor klinikaga jami qancha to'lagan? Faqat 300 000 dan ko'p to'laganlarni qoldiring (
HAVING) - (taksi) Har haydovchi: safarlar soni, jami daromad, o'rtacha baho (
AVG(baho)β NULL'lar avtomatik tashlanadi!) - (taksi) Har kun (
DATE(sana)) bo'yicha jami tushum; eng sertushum kunni toping (ORDER BY+LIMITqo'shing)