Aggregation, GROUP BY, HAVING
Hozirgacha har bir query allaqachon mavjud row larni qaytardi. Aggregation boshqacha ish qiladi: u ko'p row ni bittaga siqadi. "Nechta student bor?", "O'rtacha baho qancha?", "Qaysi course eng ko'p daromad keltiradi?" — analytics aslida shunday savollarni beradi, va aynan shu bobdan boshlab SQL qidiruv tilidan analytics tiliga aylanadi.
COUNT, SUM, AVG, MIN, MAX
Deyarli hamma narsani qoplaydigan beshta function:
| Function | Nima qaytaradi | NULL ni e'tiborsiz qoldiradimi? |
|---|---|---|
COUNT(*) |
Row lar soni | Yo'q — har bir row ni sanaydi |
COUNT(col) |
col dagi NULL bo'lmagan qiymatlar soni |
Ha |
SUM(col) |
Yig'indi | Ha |
AVG(col) |
O'rtacha arifmetik | Ha |
MIN(col) / MAX(col) |
Eng kichik / eng katta | Ha |
SELECT COUNT(*) AS total_enrollments, COUNT(grade) AS graded, ROUND(AVG(grade), 2) AS avg_grade, MIN(grade) AS lowest, MAX(grade) AS highest, SUM(grade) AS total_points FROM enrollments;
| total_enrollments | graded | avg_grade | lowest | highest | total_points |
|---|---|---|---|---|---|
| 2 500 | 1 826 | 72.98 | 38 | 100 | 133 255 |
1 qator
1 ms
total_enrollmentsgradedavg_gradelowesthighesttotal_points
201479.43471001112
Yigirmata row kirdi, bitta row chiqdi. Aggregate ni belgilaydigan asosiy xususiyat shu: u row lar to'plamini iste'mol qilib, bitta qiymat hosil qiladi.
COUNT(*) vs COUNT(col) vs COUNT(DISTINCT col)
Deyarli bir xil ko'rinadigan va uchta har xil savolga javob beradigan uchta function. Bu intervyularda doim so'raladi.
SELECT COUNT(*) AS rows_total, -- 10 COUNT(city) AS cities_present, -- 9 (Sevara ning NULL i tashlandi) COUNT(DISTINCT city) AS unique_cities -- 6 (NULL umuman sanalmaydi) FROM students;
| rows_total | cities_present | unique_cities |
|---|---|---|
| 300 | 292 | 8 |
1 qator
0 ms
IfodaNatijaQaysi savolga javob beradi
COUNT(*)10Nechta student bor?
COUNT(city)9Nechta student ning shahri yozilgan?
COUNT(DISTINCT city)6Biz nechta har xil shaharga xizmat qilamiz?
:::tip 💡 Tayyor data-quality tekshiruvi
COUNT(*) - COUNT(col) — o'sha column dagi NULL lar soni. Bitta query bilan har bir column ning to'liqligini bir vaqtda olasiz — notanish table ga birinchi qarash uchun juda foydali:
SELECT COUNT(*) AS rows, COUNT(*) - COUNT(city) AS missing_city, COUNT(*) - COUNT(gender) AS missing_gender FROM students;
:::
Aggregate lar NULL bilan qanday ishlaydi
COUNT(*) dan boshqa har bir aggregate NULL larni indamay tashlab ketadi. Odatda bu to'g'ri — lekin u maxrajni o'zgartiradi, va talabalar buni bilishi shart.
Bir xil data, ikkita javob, ikkalasi ham asoslanadi — lekin ular har xil narsani anglatadi. Avval biznes savolini so'rang: "bizda bor baholarning o'rtachasi" (79.43) mi, yoki "har bir enrollment ga to'g'ri keladigan o'rtacha ball, baholanmagani hech narsa deb hisoblanadi" (55.60) mi? Birinchisi deyarli doim kerak bo'lgan narsa; ikkinchisi deyarli doim xato.
SELECT SUM(grade) FROM enrollments WHERE status = 'nonexistent'; 0 emas, NULL qaytaradi — qo'shadigan hech narsa bo'lmagan. Dashboard ga raqam kerak bo'lsa, o'rang: COALESCE(SUM(grade), 0). COUNT bu yerda istisno: u bo'sh to'plam uchun to'g'ri 0 qaytaradi.
GROUP BY
GROUP BY "butun table uchun bitta son" g'oyasini olib, uni har bir guruhga qo'llaydi. "O'rtacha baho" o'rniga "har bir course uchun o'rtacha baho" olasiz.
SELECT course_id, COUNT(*) AS enrollments, COUNT(grade) AS graded, ROUND(AVG(grade), 2) AS avg_grade, MAX(grade) AS best FROM enrollments GROUP BY course_id ORDER BY course_id;
| course_id | enrollments | graded | avg_grade | best |
|---|---|---|---|---|
| 1 | 193 | 132 | 79.15 | 100 |
| 2 | 186 | 146 | 74.27 | 96 |
| 3 | 174 | 128 | 63.59 | 86 |
| 4 | 167 | 119 | 74.18 | 100 |
| 5 | 167 | 125 | 69.37 | 94 |
| 6 | 174 | 118 | 75.17 | 94 |
| 7 | 165 | 122 | 79.89 | 100 |
| 8 | 170 | 131 | 75.15 | 100 |
| 9 | 161 | 118 | 76.1 | 97 |
| 10 | 179 | 147 | 73.52 | 100 |
| 11 | 200 | 140 | 68.34 | 92 |
| 13 | 187 | 127 | 73.86 | 97 |
| 14 | 189 | 135 | 74.65 | 100 |
| 15 | 188 | 138 | 65.59 | 91 |
14 qator
1 ms
course_idenrollmentsgradedavg_gradebest
15468.092 25587.2100 33276.589 43280.081 54191.091
:::gotcha ⚠️ 6-course yo'q — 5-course esa chalg'ituvchi Bitta natija jadvalida ikkita saboq:
- "Deep Learning" (6-course) umuman ko'rinmaydi.
GROUP BYfaqat mavjud row larni guruhlay oladi, 6-course da esa enrollment yo'q. Shu tarzda qurilgan "course lar hisoboti" hech kim yozilmagan har bir course ni indamay tashlab ketadi — holbuki rahbariyat aynan shu haqda eshitmoqchi bo'lgan. Yechim —coursesdanLEFT JOIN(5-bob). - 5-course to'rtta enrollment dan bitta baho asosida 91.0 ko'rsatyapti. Kichik namunadagi o'rtachalar ishonchli ko'rinadi, lekin unday emas. Hisobotda
AVGyoniga doimCOUNTqo'ying, shunda o'quvchi son qanchalik qimmatga ega ekanini ko'radi. :::
Matnli column bo'yicha guruhlash ham xuddi shunday ishlaydi:
SELECT status, COUNT(*) AS n FROM enrollments GROUP BY status ORDER BY n DESC;
| status | n |
|---|---|
| completed | 1 634 |
| active | 472 |
| dropped | 202 |
| failed | 192 |
4 qator
1 ms
:::tip 💡 Bu — value_counts()
pandas dan kelgan talabalar buni darrov taniydi: GROUP BY col + COUNT(*) aynan df['col'].value_counts(). Bu bog'lanishni erta ko'rsatish butun bobni tushunarli qiladi.
:::
NULL esa o'z guruhini hosil qiladi — SQL barcha NULL larni teng deb hisoblaydigan yagona joy:
SELECT COALESCE(city, '(unknown)') AS city, COUNT(*) AS students FROM students GROUP BY city ORDER BY students DESC, city; -- Tashkent 3, Samarkand 2, keyin Andijan/Bukhara/Fergana/Namangan/(unknown) 1 tadan
| city | students |
|---|---|
| Tashkent | 82 |
| Samarkand | 40 |
| Bukhara | 38 |
| Fergana | 36 |
| Namangan | 28 |
| Andijan | 24 |
| Karshi | 22 |
| Nukus | 22 |
| (unknown) | 8 |
9 qator
0 ms
GROUP BY ning oltin qoidasi
:::info 📌 Qoida
SELECT dagi har bir column yo GROUP BY da bo'lishi, yo aggregate function ichida bo'lishi shart. Boshqasiga ruxsat yo'q.
:::
Sababi yuqoridagi diagrammaga qarasangiz oddiy. Guruhlashdan keyin 1-course to'rtta kirish row idan qurilgan bitta chiqish row i. student_id so'rasangiz, savolning javobi yo'q — u yerda to'rtta har xil student bor edi. Qaysi birini chop etsin?
-- XATO: student_id na guruhlangan, na aggregate qilingan SELECT course_id, student_id, AVG(grade) FROM enrollments GROUP BY course_id; -- PostgreSQL: XATO — "student_id" column i GROUP BY da bo'lishi kerak -- SQLite/MySQL: xato YO'Q — ular tasodifiy qiymat o'ylab topadi. Bu ancha xavfli.
| course_id | student_id | AVG(grade) |
|---|---|---|
| 1 | 59 | 79.152 |
| 2 | 241 | 74.267 |
| 3 | 85 | 63.586 |
| 4 | 211 | 74.176 |
| 5 | 281 | 69.368 |
| 6 | 21 | 75.169 |
| 7 | 130 | 79.893 |
| 8 | 33 | 75.145 |
| 9 | 215 | 76.102 |
| 10 | 102 | 73.524 |
| 11 | 216 | 68.343 |
| 13 | 221 | 73.858 |
| 14 | 188 | 74.652 |
| 15 | 95 | 65.594 |
14 qator
1 ms
:::gotcha ⚠️ SQLite va MySQL bunga yo'l qo'yadi
PostgreSQL va SQL Server query ni rad etadi. SQLite va eski MySQL esa hech qanday ogohlantirishsiz qandaydir student_id ni qaytaradi — engine oxirgi tegib o'tgan row nikini. Talabalar keyin real ko'rinadigan, lekin ma'nosiz qiymatlar bilan to'la hisobotni topshiradi. Engine nimaga yo'l qo'yishidan qat'i nazar, qoidani mutlaq deb o'rgating.
:::
Aslida kerak bo'lgan narsani olishning uchta to'g'ri yo'li:
-- (a) uni ham guruhlash — har bir (course, student) uchun bitta row SELECT course_id, student_id, AVG(grade) FROM enrollments GROUP BY course_id, student_id; -- (b) uni aggregate qilish — masalan nechta har xil student SELECT course_id, COUNT(DISTINCT student_id) AS students, AVG(grade) FROM enrollments GROUP BY course_id; -- (c) aslida "har bir course dagi eng yaxshi student" kerak edi → window function, 7-bob
1-natija
| course_id | student_id | AVG(grade) |
|---|---|---|
| 1 | 3 | 77 |
| 1 | 6 | 84 |
| 1 | 8 | 66 |
| 1 | 10 | 75 |
| 1 | 11 | NaN |
| 1 | 12 | 93 |
| 1 | 13 | NaN |
| 1 | 14 | 77 |
| 1 | 19 | 69 |
| 1 | 20 | 77 |
| 1 | 21 | 76 |
| 1 | 22 | 77 |
| 1 | 23 | 74.333 |
| 1 | 24 | 100 |
| 1 | 26 | 96 |
… 1866 qatordan birinchi 15 tasi
1866 qator
2-natija
| course_id | students | AVG(grade) |
|---|---|---|
| 1 | 134 | 79.152 |
| 2 | 134 | 74.267 |
| 3 | 124 | 63.586 |
| 4 | 121 | 74.176 |
| 5 | 130 | 69.368 |
| 6 | 133 | 75.169 |
| 7 | 129 | 79.893 |
| 8 | 127 | 75.145 |
| 9 | 129 | 76.102 |
| 10 | 137 | 73.524 |
| 11 | 152 | 68.343 |
| 13 | 136 | 73.858 |
| 14 | 148 | 74.652 |
| 15 | 132 | 65.594 |
14 qator
3 ms
Bir nechta column bo'yicha guruhlash Bir nechta column sanab o'tish har bir birikma uchun bitta guruh hosil qiladi. Chiqish row lari soni nazariy juftliklar soniga emas, haqiqatan uchraydigan birikmalar soniga teng.
-- "Har bir shaharda qaysi jinsdan nechta student bor?" SELECT COALESCE(city, '(unknown)') AS city, gender, COUNT(*) AS students FROM students GROUP BY city, gender ORDER BY city, gender;
| city | gender | students |
|---|---|---|
| (unknown) | F | 2 |
| (unknown) | M | 6 |
| Andijan | F | 10 |
| Andijan | M | 14 |
| Bukhara | F | 18 |
| Bukhara | M | 20 |
| Fergana | F | 16 |
| Fergana | M | 20 |
| Karshi | F | 11 |
| Karshi | M | 11 |
| Namangan | F | 15 |
| Namangan | M | 13 |
| Nukus | F | 15 |
| Nukus | M | 7 |
| Samarkand | F | 20 |
… 18 qatordan birinchi 15 tasi
18 qator
0 ms
gender ni row emas, column qilib olish — ya'ni pivot table — uchun SUM ni CASE WHEN bilan qo'shing (8-bob):
sql run
SELECT COALESCE(city,'(unknown)') AS city,
SUM(CASE WHEN gender = 'M' THEN 1 ELSE 0 END) AS male,
SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END) AS female,
COUNT(*) AS total
FROM students
GROUP BY city;
Bu SUM(CASE WHEN ...) pattern i real analytics ishida eng ko'p ishlatiladigan konstruksiyalardan biri. Mashq qildirishga arziydi.
HAVING va WHERE
1-bobdagi qoida, endi sababi ko'rinib turibdi: WHERE guruhlashdan oldin row larni, HAVING guruhlashdan keyin guruhlarni filtrlaydi.
-- "Faqat tugatilgan enrollment lar orasida qaysi course larning o'rtachasi 80 dan yuqori?" SELECT course_id, COUNT(*) AS completed_count, ROUND(AVG(grade), 2) AS avg_grade FROM enrollments WHERE status = 'completed' -- ROW larni filtrlaydi: active/failed/dropped tashlanadi GROUP BY course_id HAVING AVG(grade) > 80 -- GURUH larni filtrlaydi: past o'rtachali course lar tashlanadi ORDER BY avg_grade DESC;
| course_id | completed_count | avg_grade |
|---|---|---|
| 7 | 120 | 80.25 |
1 qator
1 ms
course_idcompleted_countavg_grade
5191.0 2587.2 1283.5
4-course yo'q: uning tugatilgan baholari 81 va 79, o'rtachasi aynan 80.0, > 80 esa qat'iy. 3-course ning o'rtachasi 76.5. Shuningdek e'tibor bering: 1-course ning o'rtachasi 68.0 dan 83.5 ga ko'tarildi — chunki WHERE status = 'completed' avval ikkita yiqilgan bahoni olib tashladi. WHERE ni o'zgartirish guruhlar ichidagi narsani o'zgartiradi.
Bu ikkalasi bir xil javob beradi:
WHERE status = 'completed' GROUP BY course_id -- tez
GROUP BY course_id HAVING status = 'completed' -- sekin / noto'g'ri
Ikkinchisi har bir row dan har bir guruhni quradi va shundan keyingina ishni tashlab yuboradi — qat'iy database da esa u shunchaki xato, chunki status guruhlanmagan. Iloji boricha erta filtrlang. Agar shartda aggregate ishtirok etmasa, u WHERE ga tegishli.
sql run
SELECT student_id, course_id, COUNT(*) AS times
FROM enrollments
GROUP BY student_id, course_id
HAVING COUNT(*) > 1;
Qaytgan har bir row — bitta course ga ikki marta yozilgan student. Bu GROUP BY ... HAVING COUNT(*) > 1 pattern i dublikat topishning standart query si va juda ko'p uchraydigan intervyu topshirig'i. Bizning toza data da u hech narsa qaytarmaydi.
ROUND va butun bo'lish tuzog'i
2-bobdagi tuzoq shu yerda qaytadi, va aggregation da u haqiqiy zarar keltiradi.
-- "Enrollment larning necha foizi tugatilgan?" -- XATO: butun arifmetikada 12 / 20 = 0 SELECT COUNT(*) / (SELECT COUNT(*) FROM enrollments) * 100 AS pct FROM enrollments WHERE status = 'completed'; -- 0 qaytaradi -- TO'G'RI: bo'lishdan oldin * 1.0 bilan kasrga majburlash SELECT ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM enrollments), 1) AS pct FROM enrollments WHERE status = 'completed'; -- 60.0 qaytaradi
1-natija
| pct |
|---|
| 0 |
1 qator
2-natija
| pct |
|---|
| 65.4 |
1 qator
1 ms
"Shartga mos row larning foizi" uchun ixcham idiom, yodlashga arziydi:
-- har bir course bo'yicha o'tish foizi, bitta query da SELECT course_id, COUNT(*) AS graded, ROUND(100.0 * SUM(CASE WHEN grade >= 60 THEN 1 ELSE 0 END) / COUNT(*), 1) AS pass_rate_pct FROM enrollments WHERE grade IS NOT NULL GROUP BY course_id ORDER BY pass_rate_pct DESC;
| course_id | graded | pass_rate_pct |
|---|---|---|
| 1 | 132 | 98.5 |
| 7 | 122 | 98.4 |
| 13 | 127 | 96.9 |
| 6 | 118 | 96.6 |
| 9 | 118 | 94.9 |
| 10 | 147 | 94.6 |
| 4 | 119 | 93.3 |
| 14 | 135 | 92.6 |
| 2 | 146 | 91.8 |
| 8 | 131 | 90.1 |
| 5 | 125 | 88 |
| 11 | 140 | 82.9 |
| 15 | 138 | 73.2 |
| 3 | 128 | 63.3 |
14 qator
1 ms
course_idgradedpass_rate_pct
25100.0 32100.0 42100.0 51100.0 1450.0
WHERE grade IS NOT NULL ga e'tibor bering: usiz baholanmagan enrollment lar maxrajda qolib, har bir o'tish foizini pastga tortadi. Maxrajga nima kirishini hal qilish — aslida asosiy analitik ish; SQL esa oson qismi.
Mashqlar to'plami
-- 1. Har bir shaharda nechta student? (noma'lum aniq ko'rsatilsin) SELECT COALESCE(city, '(unknown)') AS city, COUNT(*) AS n FROM students GROUP BY city ORDER BY n DESC; -- 2. Kategoriya bo'yicha course larning o'rtacha narxi SELECT category, COUNT(*) AS courses, ROUND(AVG(price)) AS avg_price FROM courses GROUP BY category ORDER BY avg_price DESC; -- 3. Har bir student ning o'rtacha bahosi, eng yaxshilari birinchi SELECT student_id, COUNT(grade) AS graded, ROUND(AVG(grade), 1) AS avg_grade FROM enrollments GROUP BY student_id HAVING COUNT(grade) > 0 ORDER BY avg_grade DESC; -- 4. 3 tadan ko'p enrollment i bor course lar SELECT course_id, COUNT(*) AS n FROM enrollments GROUP BY course_id HAVING COUNT(*) > 3; -- 5. Oylar bo'yicha enrollment soni SELECT strftime('%Y-%m', enrolled_at) AS month, COUNT(*) AS n FROM enrollments GROUP BY month ORDER BY month; -- 6. Har bir course bo'yicha tugatish foizi SELECT course_id, COUNT(*) AS total, SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS completed, ROUND(100.0 * SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) / COUNT(*), 1) AS pct FROM enrollments GROUP BY course_id ORDER BY pct DESC; -- 7. Data-quality tekshiruvi: column lardagi NULL lar SELECT COUNT(*) AS rows_total, COUNT(*) - COUNT(city) AS missing_city, COUNT(*) - COUNT(birth_date) AS missing_birth_date FROM students; -- 8. Dublikat enrollment bormi? (hech narsa qaytmasligi kerak) SELECT student_id, course_id, COUNT(*) AS times FROM enrollments GROUP BY student_id, course_id HAVING COUNT(*) > 1;
1-natija
| city | n |
|---|---|
| Tashkent | 82 |
| Samarkand | 40 |
| Bukhara | 38 |
| Fergana | 36 |
| Namangan | 28 |
| Andijan | 24 |
| Nukus | 22 |
| Karshi | 22 |
| (unknown) | 8 |
9 qator
2-natija
| category | courses | avg_price |
|---|---|---|
| Business | 3 | 1 366 667 |
| Math | 3 | 1 216 667 |
| Data | 4 | 1 175 000 |
| Programming | 3 | 1 166 667 |
| Design | 2 | 900 000 |
5 qator
3-natija
| student_id | graded | avg_grade |
|---|---|---|
| 197 | 3 | 87.3 |
| 1 | 3 | 86.3 |
| 116 | 2 | 84 |
| 107 | 2 | 84 |
| 6 | 8 | 82.8 |
| 270 | 2 | 82.5 |
| 176 | 4 | 82.3 |
| 103 | 5 | 82.2 |
| 276 | 4 | 81.8 |
| 248 | 9 | 81.6 |
| 247 | 3 | 81 |
| 130 | 6 | 81 |
| 199 | 4 | 80.8 |
| 167 | 6 | 80.8 |
| 298 | 3 | 80.3 |
… 300 qatordan birinchi 15 tasi
300 qator
4-natija
| course_id | n |
|---|---|
| 1 | 193 |
| 2 | 186 |
| 3 | 174 |
| 4 | 167 |
| 5 | 167 |
| 6 | 174 |
| 7 | 165 |
| 8 | 170 |
| 9 | 161 |
| 10 | 179 |
| 11 | 200 |
| 13 | 187 |
| 14 | 189 |
| 15 | 188 |
14 qator
5-natija
| month | n |
|---|---|
| 2025-02 | 1 077 |
| 2025-03 | 131 |
| 2025-09 | 1 188 |
| 2025-10 | 104 |
4 qator
6-natija
| course_id | total | completed | pct |
|---|---|---|---|
| 10 | 179 | 139 | 77.7 |
| 7 | 165 | 120 | 72.7 |
| 2 | 186 | 134 | 72 |
| 9 | 161 | 112 | 69.6 |
| 8 | 170 | 118 | 69.4 |
| 1 | 193 | 130 | 67.4 |
| 4 | 167 | 111 | 66.5 |
| 14 | 189 | 125 | 66.1 |
| 5 | 167 | 110 | 65.9 |
| 13 | 187 | 123 | 65.8 |
| 6 | 174 | 114 | 65.5 |
| 11 | 200 | 116 | 58 |
| 15 | 188 | 101 | 53.7 |
| 3 | 174 | 81 | 46.6 |
14 qator
7-natija
| rows_total | missing_city | missing_birth_date |
|---|---|---|
| 300 | 8 | 0 |
1 qator
8-natija
| student_id | course_id | times |
|---|---|---|
| 1 | 3 | 2 |
| 1 | 11 | 2 |
| 2 | 11 | 2 |
| 3 | 6 | 2 |
| 3 | 11 | 2 |
| 4 | 7 | 2 |
| 4 | 15 | 2 |
| 5 | 2 | 2 |
| 5 | 5 | 2 |
| 6 | 4 | 2 |
| 6 | 14 | 2 |
| 7 | 9 | 2 |
| 8 | 1 | 2 |
| 10 | 14 | 2 |
| 11 | 7 | 2 |
… 514 qatordan birinchi 15 tasi
514 qator
5 ms
Intervyu savollari
S: COUNT(*), COUNT(column) va COUNT(1) farqi?
COUNT(*) row larni sanaydi, hatto butunlay NULL row larni ham. COUNT(column) faqat o'sha column NULL bo'lmagan row larni sanaydi. COUNT(1) zamonaviy engine larda COUNT(*) bilan aynan bir xil — uning tezroq degan eski qarash afsona.
S: AVG NULL larni hisobga oladimi?
Yo'q. U NULL bo'lmagan qiymatlar soniga bo'ladi. NULL lar nol deb hisoblanishini xohlasangiz, buni aniq yozishingiz kerak: AVG(COALESCE(grade, 0)) — lekin biznes haqiqatan shuni nazarda tutayaptimi, so'rang.
S: Nol row ustida SUM nima qaytaradi?
0 emas, NULL. Raqam kerak bo'lsa, COALESCE(SUM(x), 0) ga o'rang. COUNT esa to'g'ri 0 qaytaradi.
S: WHERE va HAVING?
WHERE GROUP BY dan oldin row larni filtrlaydi va aggregate ishlata olmaydi. HAVING GROUP BY dan keyin guruhlarni filtrlaydi va ishlata oladi. Aggregate talab qilmaydigan har qanday shart WHERE ga tegishli, chunki ertaroq filtrlash kamroq ish degani.
S: GROUP BY siz HAVING ishlatsa bo'ladimi?
Ha. U holda butun table bitta guruh deb qaraladi: SELECT AVG(grade) FROM enrollments HAVING AVG(grade) > 70; bitta row yoki hech narsa qaytaradi.
S: Nega SELECT course_id, student_id, AVG(grade) ... GROUP BY course_id noto'g'ri?
Chunki guruhda ko'p har xil student_id bor, chop etish uchun esa faqat bittasi mumkin. PostgreSQL xato beradi; SQLite va MySQL tasodifiy qiymat qaytaradi, bu esa battarroq — natija to'g'ridek ko'rinadi, lekin ma'nosiz.
S: Dublikat row larni qanday topasiz?
Unique bo'lishi kerak bo'lgan column lar bo'yicha GROUP BY, keyin HAVING COUNT(*) > 1.
S: Nega "mahsulot bo'yicha savdo" hisobotida ba'zi mahsulotlar yo'q bo'lishi mumkin?
Chunki GROUP BY faqat mavjud row larni ko'radi. Savdosi nol bo'lgan mahsulotning guruhlanadigan row i yo'q. Uni 0 bilan hisobotda qoldirish uchun products table idan LEFT JOIN kerak.
Xulosa
- Aggregate lar ko'p row ni bittaga siqadi:
COUNT,SUM,AVG,MIN,MAX. COUNT(*)= row lar ·COUNT(col)= NULL bo'lmagan qiymatlar ·COUNT(DISTINCT col)= har xil NULL bo'lmagan qiymatlar. Ularning farqi — bepul NULL tekshiruvi.COUNT(*)dan boshqa har bir aggregate NULL larni tashlaydi, bu esaAVGning maxrajini o'zgartiradi. Nol row ustidaSUM0 emas, NULL.GROUP BYhar bir guruh uchun bitta chiqish row i yasaydi. U row i yo'q guruhlarni ko'rsata olmaydi — buning uchunLEFT JOINkerak.- Oltin qoida: tanlangan har bir column yo
GROUP BYda, yo aggregate ichida. SQLite/MySQL buzishga yo'l qo'yadi; buzmang. WHEREguruhlashdan oldin,HAVINGkeyin. Iloji boricha erta filtrlang.GROUP BY ... HAVING COUNT(*) > 1— standart dublikat topuvchi.* 1.0yoki* 100.0bilan kasrga majburlang;SUM(CASE WHEN ... THEN 1 ELSE 0 END)— foiz idiomi.AVGyoniga doimCOUNTni chiqaring, shunda hech kim bitta qiymatdan yasalgan o'rtachaga ishonmaydi.
Keyingi: 5-bob — JOIN lar. Hozirgacha har bir hisobot bitta table ichida qamalgan edi; JOIN lar esa bitta natijada "student ismi va course nomi" deyish imkonini beradi.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.