IlmHamroh
JavaScript Full-stack/26-qism. SQL va PostgreSQL20/64-dars17 daqiqa
Mundarija (28)

SQL GROUP BY va agregatlar: COUNT, SUM, AVG, HAVING va FILTER

Qisqacha: Agregat funksiya (count, sum, avg, min, max) ko'p qatorni bitta qiymatga aylantiradi. GROUP BY ustun qatorlarni shu ustun bo'yicha guruhlaydi va agregatni har guruh uchun alohida hisoblaydi. WHERE guruhlashdan oldin qatorlarni filtrlaydi, HAVING esa keyin guruhlarni. PostgreSQL'ning FILTER (WHERE …) i bitta so'rovda turli shartli sanoqlarni beradi; string_agg, array_agg, json_agg esa guruhni matn, massiv yoki JSON'ga yig'adi.

Bu darsda

  • count(*), count(ustun), count(DISTINCT …), sum, avg, min, max ni farqlaysiz.
  • GROUP BY bilan "har foydalanuvchi bo'yicha" statistika olasiz va guruhlanmagan ustun xatosini tushunasiz.
  • WHERE va HAVING farqini bajarilish tartibi orqali tushuntira olasiz.
  • FILTER bilan bir so'rovda bir nechta shartli sanoq yozasiz.
  • string_agg, array_agg, json_agg bilan guruhni bitta qiymatga yig'asiz — API javobi shaklida.

Oldin bilishingiz kerak: NULL mantiqi, SELECT va WHERE, ORDER BY, LIMIT va OFFSET.

1. Nega bu kerak?

vazifalar ilovasining "Statistika" sahifasi: "Jami 9 vazifa, 4 tasi bajarilgan (44%)". Admin panelida: "eng faol foydalanuvchilar". Bu raqamlar qayerdan keladi?

Bitta yo'l — hamma vazifalarni ilovaga olib, JavaScript'da sanash: vazifalar.filter(v => v.bajarildi).length. 40 qatorda ishlaydi. 2 million qatorda esa ilova 300 MB ma'lumotni tarmoq orqali tortib, bitta son uchun xotirani to'ldiradi.

To'g'ri yo'l — bazadan tayyor sonni so'rash. Baza ma'lumot yonida hisoblaydi va faqat natijani yuboradi: bitta qator, bir necha bayt.

NULL mantiqi darsida agregatlarni bir oz ko'rdik: count(*) va count(x) farq qiladi, bo'sh sum — NULL. Endi ularni guruhlar bilan birga ishlatamiz.

O'xshatish: maktabdagi sinf jurnali. Direktor "har sinfda nechta a'lochi bor?" deb so'raydi. O'qituvchi har bir o'quvchini direktorga olib kirmaydi. U jurnalni sinflarga ajratadi (guruhlash), har sinfda sanaydi (agregat) va bitta ro'yxat beradi: "9-A — 5 ta, 9-B — 3 ta".

2. Agregat funksiyalar

2.1 Butun jadval bo'yicha

Bu darsdagi misollar holat 15 bazasida. Kanon (PostgreSQL 18.6):

sql
SELECT count(*) AS jami,
       count(*) FILTER (WHERE bajarildi) AS bajarilgan,
       round(100.0 * count(*) FILTER (WHERE bajarildi)
             / count(*), 1) AS foiz,
       min(yaratilgan_vaqt) AS birinchi,
       max(yaratilgan_vaqt) AS oxirgi
FROM vazifalar;

Natija:

text
 jami | bajarilgan | foiz |        birinchi        |         oxirgi         
------+------------+------+------------------------+------------------------
   40 |         20 | 50.0 | 2026-10-01 03:30:00+00 | 2026-10-19 02:30:00+00
(1 row)

40 qatordan bitta qator chiqdi. FILTER ni hozircha "faqat bajarilganlarni sana" deb o'qing. Uni keyinroq alohida ko'ramiz. round(…, 1) — bir xonagacha yaxlitlash (Ifodalar va o'rnatilgan funksiyalar). 100.0 dagi .0 muhim: 100 * 20 / 40 butun sonlar bo'linmasi bo'lardi va kasr qismi yo'qolardi.

min va max faqat sonlarda emas, vaqt va matnda ham ishlaydi: eng erta va eng kech vaqt.

2.2 Asosiy agregatlar

Biz ishga tushirdik (PostgreSQL 17.6, Windows 11) — vazifa matnlari uzunligi bo'yicha:

sql
SELECT round(avg(char_length(matn)), 1) AS ortacha_uzunlik,
       min(char_length(matn)) AS eng_qisqa,
       max(char_length(matn)) AS eng_uzun,
       sum(char_length(matn)) AS jami_belgi
FROM vazifalar;

Natija:

text
 ortacha_uzunlik | eng_qisqa | eng_uzun | jami_belgi 
-----------------+-----------+----------+------------
            35.2 |        19 |       48 |       1409
(1 row)
Funksiya Nima qaytaradi NULL
count(*) Qatorlar soni Hisobga oladi
count(ustun) Ustuni NULL bo'lmagan qatorlar Tashlaydi
count(DISTINCT ustun) Noyob qiymatlar soni Tashlaydi
sum, avg Yig'indi, o'rtacha Tashlaydi
min, max Eng kichik, eng katta Tashlaydi

count(DISTINCT …) ni ko'ramiz — biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT count(*) AS vazifalar,
       count(DISTINCT egasi_id) AS egalar
FROM vazifalar;

Natija:

text
 vazifalar | egalar 
-----------+--------
        40 |      7
(1 row)

40 vazifa, lekin ularning egalari 7 kishi (Shahnozada vazifa yo'q).

Maslahat: Node'dagi pg drayveri count(*) natijasini matn ('40') qilib qaytaradi, son emas — chunki count bigint turida, JavaScript number esa juda katta butun sonlarni aniq saqlay olmaydi. Buni node-postgres asoslari darsida hal qilamiz.

3. GROUP BY: guruhlab sanash

3.1 Bitta ustun bo'yicha

"Har foydalanuvchida nechta vazifa?" Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT egasi_id, count(*) AS jami
FROM vazifalar
GROUP BY egasi_id
ORDER BY egasi_id;

Natija:

text
 egasi_id | jami 
----------+------
        1 |    9
        2 |    6
        3 |    6
        4 |    5
        5 |    6
        6 |    4
        7 |    4
(7 rows)

Grafikda xuddi shu sonlar — ustunlarning balandligi har guruhdagi qatorlar soni:

Har foydalanuvchining vazifalari (holat 15)
  • Aziz9 ta
  • Dilnoza6 ta
  • Sardor6 ta
  • Madina5 ta
  • Javohir6 ta
  • Gulnora4 ta
  • Bekzod4 ta

Manba: vazifalar namuna bazasi (holat 15), SELECT egasi_id, count(*) … GROUP BY egasi_id — PostgreSQL 17.6 va kanon 18.6 da bir xil, 2026-10-24

GROUP BY egasi_id qanday ishlaydi:

flowchart LR
  A["40 qator"] --> B["egasi_id bo'yicha<br/>7 guruhga ajratish"]
  B --> C["Har guruhda<br/>count(*)"]
  C --> D["7 qator:<br/>egasi_id, jami"]

Diagrammadagi o'rta qadamga qarang: guruhlashdan keyin har guruh bitta qatorga aylanadi. Natijadagi qatorlar soni = guruhlar soni.

Ikki ustun bo'yicha ham guruhlash mumkin — har noyob juftlik bitta guruh. Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT egasi_id, bajarildi, count(*)
FROM vazifalar
WHERE egasi_id <= 2
GROUP BY egasi_id, bajarildi
ORDER BY 1, 2;

Natija:

text
 egasi_id | bajarildi | count 
----------+-----------+-------
        1 | f         |     5
        1 | t         |     4
        2 | f         |     3
        2 | t         |     3
(4 rows)

Taxallus bermasak, ustun nomi funksiya nomi bo'ladi (count).

3.2 Guruhlanmagan ustun — xato

Ko'p uchraydigan urinish: "har foydalanuvchining vazifalari soni va matni". Kanon (PostgreSQL 18.6):

sql
SELECT egasi_id, matn, count(*)
FROM vazifalar
GROUP BY egasi_id;

Natija:

text
ERROR:  column "vazifalar.matn" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT egasi_id, matn, count(*)
                         ^

Tarjimasi: "vazifalar.matn ustuni GROUP BY da bo'lishi yoki agregat funksiya ichida ishlatilishi kerak".

Mantiqi: Azizning guruhida 9 qator, 9 xil matn. Natijada esa Aziz uchun bitta qator. Qaysi matnni yozish kerak? Baza buni bilmaydi va taxmin qilmaydi. Ikki yo'l bor:

  • matn ni ham GROUP BY ga qo'shish — lekin unda har (ega, matn) alohida guruh bo'ladi va sanoq ma'nosini yo'qotadi;
  • matnlarni agregat bilan yig'ish — string_agg(matn, ', ') (keyingi bo'limlarda).

Diqqat: MySQL'ning eski sozlamasi (ONLY_FULL_GROUP_BY o'chiq) bunday so'rovni xatosiz bajarib, tasodifiy matnni qaytaradi. PostgreSQL bunga yo'l qo'ymaydi — bu yaxshi.

Bitta istisno bor: guruhlash ustuni birlamchi kalit bo'lsa, o'sha jadvalning boshqa ustunlarini ham yozish mumkin. GROUP BY f.id dan keyin f.ism ruxsat: bitta id — doim bitta ism (bu funksional bog'liqlik, Normalizatsiya darsida chuqur). Kanon (PostgreSQL 18.6) buni ikki jadvalni bog'lab ko'rsatadi. JOIN ni JOIN'lar darsida o'rganamiz — hozir faqat natijaga qarang: egasi_id o'rniga ism.

sql
SELECT f.ism, count(*)
FROM foydalanuvchilar f
JOIN vazifalar v ON v.egasi_id = f.id
GROUP BY f.id
ORDER BY 2 DESC, 1
LIMIT 2;

Natija:

text
       ism        | count 
------------------+-------
 Aziz Karimov     |     9
 Dilnoza Rahimova |     6
(2 rows)

3.3 Bo'sh natija: GROUP BY bor va yo'q

Nozik farq. Shahnozaning (8) vazifalari yo'q. Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT count(*) FROM vazifalar WHERE egasi_id = 8;

SELECT egasi_id, count(*) FROM vazifalar
WHERE egasi_id = 8
GROUP BY egasi_id;

Natija:

text
 count 
-------
     0
(1 row)

 egasi_id | count 
----------+-------
(0 rows)

GROUP BY siz agregat doim bitta qator qaytaradi — bo'sh to'plamda ham (0). GROUP BY bilan esa bo'sh to'plamda guruh yo'q — demak qator ham yo'q. "Har foydalanuvchining vazifalari soni"da Shahnoza nol bilan ko'rinishi uchun foydalanuvchilar jadvalidan boshlash kerak (LEFT JOIN) — JOIN'lar darsining birinchi misollaridan biri.

Tekshirib ko'ring: SELECT bajarildi, count(*) FROM vazifalar GROUP BY bajarildi nechta qator qaytaradi va qaysi sonlar?

Javob

2 qator: f — 20, t — 20. bajarildi ning ikkita noyob qiymati bor — ikkita guruh. Ustunda NULL bo'lsa, u alohida, uchinchi guruh bo'lardi: GROUP BY uchun hamma NULL lar bitta guruh.

4. WHERE va HAVING

4.1 Ikki xil filtr

"6 va undan ko'p vazifasi bor foydalanuvchilar" — shart sanoqqa qo'yiladi. Birinchi urinish — shartni odatdagidek WHERE ga yozamiz. Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT egasi_id, count(*) FROM vazifalar
WHERE count(*) >= 6 GROUP BY egasi_id;

Natija:

text
ERROR:  aggregate functions are not allowed in WHERE
LINE 2: WHERE count(*) >= 6 GROUP BY egasi_id;
              ^

Tarjimasi: "WHERE ichida agregat funksiyalarga ruxsat yo'q". Sababi — bajarilish tartibi. SELECT va WHERE darsidagi zanjirni guruhlash bilan to'ldiramiz:

flowchart LR
  A["FROM"] --> B["WHERE<br/>qatorlar"]
  B --> C["GROUP BY"]
  C --> D["HAVING<br/>guruhlar"]
  D --> E["SELECT"]
  E --> F["ORDER BY"]
  F --> G["LIMIT"]

Chapdan o'qing: WHERE ishlayotganda guruhlar hali yo'q, count ni hisoblab bo'lmaydi. Guruh shartlari uchun alohida joy bor — HAVING, u GROUP BY dan keyin keladi.

Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT egasi_id,
       count(*) AS jami,
       count(*) FILTER (WHERE bajarildi) AS bajarilgan
FROM vazifalar
GROUP BY egasi_id
HAVING count(*) >= 6
ORDER BY jami DESC, egasi_id;

Natija:

text
 egasi_id | jami | bajarilgan 
----------+------+------------
        1 |    9 |          4
        2 |    6 |          3
        3 |    6 |          3
        5 |    6 |          3
(4 rows)

Madina (5 ta), Gulnora va Bekzod (4 tadan) — HAVING ularning guruhlarini tashladi. ORDER BY jami — taxallus ishladi, chunki ORDER BY SELECT dan keyin. HAVING jami >= 6 esa ishlamaydi — HAVING SELECT dan oldin, shuning uchun count(*) ni takror yozdik.

4.2 Ikkalasi birga

WHERE va HAVING bitta so'rovda turli ish qiladi. Kanon (PostgreSQL 18.6) — "bajarilgan vazifasi 3 tadan ko'p bo'lganlar":

sql
SELECT egasi_id, count(*)
FROM vazifalar
WHERE bajarildi
GROUP BY egasi_id
HAVING count(*) > 3
ORDER BY egasi_id;

Natija:

text
 egasi_id | count 
----------+-------
        1 |     4
(1 row)

Qadamlar: WHERE bajarildi 40 qatordan 20 tasini qoldirdi → GROUP BY ularni egalar bo'yicha ajratdi → HAVING faqat 3 tadan ko'p guruhni qoldirdi — faqat Aziz (4).

Qoida: qatorga oid shart — WHERE, guruhga oid shart (agregat bilan) — HAVING. Qatorga oid shartni HAVING ga yozish ham ishlashi mumkin, lekin sekinroq: baza avval keraksiz qatorlarni guruhlaydi, keyin tashlaydi.

Tekshirib ko'ring: "Kamida bitta bajarilmagan vazifasi bor foydalanuvchilar" — shart WHERE gami, HAVING gami?

Javob

Ikkala yo'l bor. WHERE NOT bajarildi GROUP BY egasi_id — bajarilmaganlarni qoldiradi, har egadan bitta guruh chiqadi. Yoki GROUP BY egasi_id HAVING count(*) FILTER (WHERE NOT bajarildi) >= 1. Birinchisi soddaroq va tezroq. Ikkinchisi jami sonni ham ko'rsatish kerak bo'lsa qulay.

5. FILTER: bir so'rovda bir nechta sanoq

5.1 Muammo va yechim

Statistika sahifasi uchun har foydalanuvchida uch son kerak: jami, bajarilgan, qolgan. WHERE bajarildi qo'ysak, jami sanoq buziladi. Uchta alohida so'rov — uch marta jadvalni o'qish.

FILTER (WHERE shart) — agregatga o'zining shartini beradi. Har agregat jadvalning bir xil o'qishidan o'z qatorlarini tanlaydi. Bu PostgreSQL'ning (SQL standartidagi) imkoniyati. Yuqoridagi HAVING misolida count(*) FILTER (WHERE bajarildi) aynan shu edi.

Boshqa bazalarda (MySQL) xuddi shu ish CASE bilan qilinadi: sum(CASE WHEN bajarildi THEN 1 ELSE 0 END). CASE ni Ifodalar va o'rnatilgan funksiyalar darsida ko'ramiz — FILTER o'qishga ancha qulay.

5.2 FILTER boshqa agregatlar bilan

FILTER istalgan agregatga qo'shiladi: sum(x) FILTER (…), string_agg(…) FILTER (…). Masalan "har foydalanuvchining qolgan vazifalari ro'yxati" — keyingi bo'limda.

6. Guruhni bitta qiymatga yig'ish

6.1 string_agg va array_agg

string_agg(ustun, ajratgich) — guruhdagi matnlarni bitta satrga ulaydi. array_agg(ustun) — massivga. Ikkalasida ORDER BY qavs ichida yoziladi. Kanon (PostgreSQL 18.6):

sql
SELECT egasi_id,
       string_agg(matn, ' | ' ORDER BY yaratilgan_vaqt)
         FILTER (WHERE NOT bajarildi) AS qolganlar
FROM vazifalar
WHERE egasi_id IN (2, 7)
GROUP BY egasi_id
ORDER BY egasi_id;

Natija:

text
 egasi_id |                                                qolganlar                                                 
----------+----------------------------------------------------------------------------------------------------------
        2 | IELTS writing task 2 mashqi #ingliz | Dugonamning tug'ilgan kuniga sovg'a #oila | Rezyume yangilash #ish
        7 | Universitet kontrakti #to'lov | Noutbukka SSD qo'yish #ta'mir
(2 rows)

Dilnoza (2) va Bekzod (7) ning bajarilmagan vazifalari, yaratilish tartibida, bitta satrda. Bu — «Guruhlanmagan ustun — xato» bo'limidagi muammoning to'g'ri yechimi.

array_agg — kanon (PostgreSQL 18.6):

sql
SELECT egasi_id, array_agg(id ORDER BY id) AS idlar
FROM vazifalar
GROUP BY egasi_id
ORDER BY egasi_id
LIMIT 3;

Natija:

text
 egasi_id |          idlar           
----------+--------------------------
        1 | {1,2,8,9,17,23,28,34,38}
        2 | {5,14,16,26,29,36}
        3 | {10,11,21,24,32,35}
(3 rows)

{…} — PostgreSQL massivining ko'rinishi. Node'da pg uni JavaScript massiviga aylantiradi: [1, 2, 8, …].

6.2 json_agg — API javobi bazada

API odatda JSON qaytaradi. json_build_object bitta qatordan JSON obyekt yasaydi, json_agg esa guruhdagi obyektlarni massivga yig'adi. Kanon (PostgreSQL 18.6) — Bekzodning vazifalari:

sql
SELECT json_agg(
         json_build_object('id', id, 'matn', matn,
                           'bajarildi', bajarildi)
         ORDER BY id) AS api_javobi
FROM vazifalar
WHERE egasi_id = 7;

Natija:

text
                                                                                                                                                    api_javobi                                                                                                                                                    
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id" : 19, "matn" : "Universitet kontrakti #to'lov", "bajarildi" : false}, {"id" : 25, "matn" : "Algoritmlar: 5 ta masala #o'qish", "bajarildi" : true}, {"id" : 33, "matn" : "Noutbukka SSD qo'yish #ta'mir", "bajarildi" : false}, {"id" : 39, "matn" : "Do'stlar bilan choyxona #dam", "bajarildi" : true}]
(1 row)

Telefonda gorizontal suring — bu bitta uzun satr. Bu GET /vazifalar ning tayyor javob tanasi: ilova uni o'zgartirmasdan yuborishi mumkin.

json turi kalitlar orasiga " : " qo'yadi. jsonb varianti (jsonb_agg + jsonb_build_object) ixchamroq: {"id": 19, "matn": …} — biz ishga tushirdik (PostgreSQL 17.6), natija [{"id": 19, "matn": "Universitet kontrakti #to'lov"}, …]. json va jsonb farqini PostgreSQL ma'lumot turlari darsida ko'rgansiz.

Qachon JSON'ni bazada yasash kerak? Ichma-ich tuzilma kerak bo'lganda (vazifa + uning teglari + izohlari) — bitta so'rov o'nlab so'rov o'rnini bosadi. Oddiy ro'yxat uchun esa qatorlarni qaytarib, JSON'ni ilovada yasash ham normal. Amaliyot: real loyiha sxemasi darsida teglar massivi bilan ro'yxat so'rovini yozamiz.

Tekshirib ko'ring: array_agg(id) va count(id) bir guruhda bir xil narsani "biladi". Nega ikkalasi ham kerak?

Javob

count faqat sonni beradi — ixcham va tez. array_agg qiymatlarning o'zini — ro'yxat kerak bo'lganda. 10 000 vazifali guruhda array_agg katta massiv yasaydi; faqat son kerak bo'lsa — count.

7. Vaqt bo'yicha guruhlash (ko'prik)

Hisobotlar ko'pincha "kunlar/haftalar bo'yicha". Kanon (PostgreSQL 18.6) — har haftada nechta vazifa yaratilgan:

sql
SELECT date_trunc('week', yaratilgan_vaqt)::date AS hafta,
       count(*)
FROM vazifalar
GROUP BY 1
ORDER BY 1;

Natija:

text
   hafta    | count 
------------+-------
 2026-09-28 |    14
 2026-10-05 |    15
 2026-10-12 |    10
 2026-10-19 |     1
(4 rows)

date_trunc('week', …) vaqtni haftaning dushanbasiga "kesadi" — bir haftadagi hamma vazifalar bir xil qiymat oladi va bitta guruhga tushadi. GROUP BY 1 — "SELECT dagi birinchi ifoda bo'yicha". Funksiyaning o'zini Ifodalar va o'rnatilgan funksiyalar darsida o'rganamiz. Muhim ogohlantirish: hafta chegarasi sessiya zonasida (bu yerda UTC) hisoblanadi. Toshkent bo'yicha to'g'ri hisobot — SQL'da sana va vaqt zonalari darsida.

8. SQL qadami: statistika so'rovi (holat 15, commit yo'q)

Sxema o'zgarmaydi. Ilovaning "Mening statistikam" bloki uchun bitta so'rov yetarli — Aziz (1) uchun. Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT count(*) AS jami,
       count(*) FILTER (WHERE bajarildi) AS bajarilgan,
       count(*) FILTER (WHERE NOT bajarildi) AS qolgan,
       max(yaratilgan_vaqt) AS oxirgi_vazifa
FROM vazifalar
WHERE egasi_id = 1;

Natija:

text
 jami | bajarilgan | qolgan |     oxirgi_vazifa      
------+------------+--------+------------------------
    9 |          4 |      5 | 2026-10-18 12:20:00+00
(1 row)

Bitta jadval o'qilishida uchta sanoq: jami 9, bajarilgan 4, qolgan 5 — 4 + 5 = 9, demak hech bir qator yo'qolmadi. GROUP BY siz — doim bitta qator, vazifasiz foydalanuvchi uchun ham (0, 0, 0 va oxirgi_vazifa — NULL). API uchun bu qulay: "topilmadi" holatini alohida tekshirish shart emas. Ilovada 1 parametr ($1) bo'ladi — Parametrli so'rovlar.

9. Ko'p uchraydigan xatolar

Xato yoki belgi Sabab Davosi
must appear in the GROUP BY clause or be used in an aggregate function SELECT da guruhlanmagan ustun GROUP BY ga qo'shing yoki agregat (string_agg)
aggregate functions are not allowed in WHERE Agregat sharti WHERE da HAVING
HAVING jami > 5 — column "jami" does not exist HAVING taxallusni bilmaydi Ifodani takrorlang
Foiz 0 yoki butun chiqdi Butun son bo'linmasi 100.0 * …
Vazifasiz foydalanuvchi natijada yo'q GROUP BY egasi_id faqat bor qiymatlarni guruhlaydi Foydalanuvchilardan LEFT JOIN (JOIN'lar)
count(ustun) kutilgandan kam NULL lar sanalmaydi count(*) yoki ataylab
sum NULL Bo'sh guruh coalesce(sum(x), 0)

10. Mashqlar

1-mashq (oson): bo'shliqlarni to'ldiring

  • (1), (NULL), (3) uchun count(*) = [:3], count(x) = [:2]
  • Guruh sharti yoziladigan joy:
  • SELECT count(*) FROM vazifalar WHERE egasi_id = 999 qatorlar soni:
  • Matnlarni bitta satrga yig'adigan agregat:
Yechim
  • 3 va 2 — count(x) NULL ni tashlaydi.
  • HAVING — GROUP BY dan keyin, agregatlar bilan.
  • 1 — GROUP BY siz agregat doim bitta qator beradi (qiymati 0).
  • string_agg — ajratgich bilan: string_agg(matn, ', ').

2-mashq (o'rta): ishi ko'plar

Har foydalanuvchi uchun jami vazifalar va bajarilmaganlar sonini chiqaring. Faqat bajarilmagani 3 va undan ko'p bo'lganlar qolsin. Bajarilmaganlar soni bo'yicha kamayish tartibida, tenglarda egasi_id bo'yicha.

Yechim
sql
SELECT egasi_id,
       count(*) AS jami,
       count(*) FILTER (WHERE NOT bajarildi) AS qolgan
FROM vazifalar
GROUP BY egasi_id
HAVING count(*) FILTER (WHERE NOT bajarildi) >= 3
ORDER BY qolgan DESC, egasi_id;

Natija (biz ishga tushirdik, PG 17.6):

text
 egasi_id | jami | qolgan 
----------+------+--------
        1 |    9 |      5
        2 |    6 |      3
        3 |    6 |      3
        5 |    6 |      3
(4 rows)

WHERE NOT bajarildi bilan yozsangiz, jami ustuni ham faqat bajarilmaganlarni sanab qolardi. FILTER jami sanoqni buzmasdan alohida shart beradi. HAVING da taxallus (qolgan) ishlamaydi — ifoda takrorlandi.

3-mashq (qiyin): o'quvchilar statistikasi va JSON

(a) Har foydalanuvchi uchun: matnida #o'qish tegi bor vazifalar soni va umumiy bajarilish foizi (bir xona aniqlikda). Faqat kamida bitta #o'qish vazifasi borlar. Foiz bo'yicha kamayish tartibida, tenglarda egasi_id bo'yicha.

(b) Madina'ning (egasi_id 4) vazifalarini bitta JSON massiv qiling: har element {"id", "bajarildi"}. Bajarilmaganlar birinchi, keyin id bo'yicha.

Yechim
sql
-- (a)
SELECT egasi_id,
       count(*) FILTER (WHERE matn LIKE '%#o''qish%') AS oqish,
       round(100.0 * count(*) FILTER (WHERE bajarildi)
             / count(*), 1) AS foiz
FROM vazifalar
GROUP BY egasi_id
HAVING count(*) FILTER (WHERE matn LIKE '%#o''qish%') > 0
ORDER BY foiz DESC, egasi_id;

-- (b)
SELECT json_agg(json_build_object('id', id, 'bajarildi', bajarildi)
                ORDER BY bajarildi, id) AS madina
FROM vazifalar
WHERE egasi_id = 4;

Natija (biz ishga tushirdik, PG 17.6):

text
 egasi_id | oqish | foiz 
----------+-------+------
        4 |     3 | 60.0
        2 |     1 | 50.0
        7 |     1 | 50.0
        1 |     3 | 44.4
(4 rows)

                                                                                 madina                                                                                 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id" : 7, "bajarildi" : false}, {"id" : 31, "bajarildi" : false}, {"id" : 13, "bajarildi" : true}, {"id" : 18, "bajarildi" : true}, {"id" : 37, "bajarildi" : true}]
(1 row)

(a): foiz hamma vazifalardan (count(*) — filtrsiz), #o'qish sanog'i esa alohida FILTER. Agar WHERE matn LIKE … qo'ysangiz, foiz faqat o'qish vazifalaridan hisoblanib qolardi — boshqa savol. (b): ORDER BY bajarildi — false true dan oldin. ORDER BY json_agg qavsi ichida — u massiv elementlari tartibini belgilaydi.

11. Real ishda

  • Dashboard va admin panellari — "bugungi buyurtmalar soni", "o'rtacha chek", "eng faol 10 foydalanuvchi" — hammasi GROUP BY + agregat. Bazada hisoblash ilovaga ma'lumot tortishdan yuzlab barobar tejamkor.
  • API javoblari — json_agg bilan ichma-ich JSON bitta so'rovda; N+1 muammosi ning yechimlaridan biri.
  • Analitika — FILTER bilan pivot jadvallar, date_trunc bilan vaqt qatorlari. Murakkabroq tahlil — Window funksiyalar va Tahliliy SQL naqshlari.
  • Intervyu savollari: "WHERE va HAVING farqi?", "count(*) va count(ustun) farqi?", "Nega SELECT da guruhlanmagan ustun yozib bo'lmaydi?" — SQL intervyularining doimiy savollari.

Xulosa

  • Agregat ko'p qatorni bitta qiymatga aylantiradi; count(*) — qatorlar, count(ustun) — NULL emaslar, count(DISTINCT …) — noyoblar.
  • GROUP BY — har guruhga bitta natija qatori; SELECT da faqat guruhlangan ustunlar va agregatlar (PK bo'yicha guruhlashda — o'sha jadval ustunlari ham).
  • GROUP BY siz agregat doim bitta qator, GROUP BY bilan bo'sh to'plam — nol qator.
  • WHERE — guruhlashdan oldin qatorlarni, HAVING — keyin guruhlarni filtrlaydi; agregat sharti faqat HAVING da.
  • FILTER (WHERE …) — bir so'rovda bir nechta shartli sanoq.
  • string_agg, array_agg, json_agg (qavs ichida ORDER BY bilan) — guruhni matn, massiv yoki API uchun JSON'ga yig'adi.

Keyingi dars: ALTER TABLE va sxemani o'zgartirish — vazifalarga ustuvorlik, muddat va kichik vazifalar qo'shamiz: ADD COLUMN, ALTER TYPE … USING, NOT VALID va bu buyruqlar qanday qulf olishi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
SQL GROUP BY va agregatlar: COUNT, SUM, AVG, HAVING va FILTER — IlmHamroh