Mundarija (27)
- Bu darsda
- 1. Nega bu kerak?
- 2. OVER va PARTITION BY
- 2.1 Butun natija bo'yicha oyna
- 2.2 Agregat ustidan oyna: ulush
- 3. Reyting funksiyalari
- 3.1 Uch funksiya, tenglar bilan
- 3.2 ROW_NUMBER tenglarda tasodifiy
- 4. Har guruhdan top-N
- 4.1 PARTITION BY + ROW_NUMBER
- 4.2 Yechim: CTE bilan filtrlash
- 4.3 RANK bilan: tenglar ham kirsin
- 5. Oyna tartibi va so'rov tartibi
- 6. Ko'p uchraydigan xatolar
- 6.1 column "n" does not exist
- 6.2 window functions are not allowed in HAVING
- 6.3 ROW_NUMBER har safar boshqacha
- 6.4 RANK kutilgandan ko'p qator berdi
- 6.5 Window GROUP BY dagi xato
- 7. Mashqlar
- 1-mashq (oson): vazifa ulushi
- 2-mashq (o'rta): har kimning birinchi vazifasi
- 3-mashq (qiyin): teglar reytingi
- 4-mashq: SQL qadami — sahifa va jami bitta so'rovda
- 8. Real ishda
- Xulosa
- Manbalar
SQL window funksiyalar: OVER, PARTITION BY, ROW_NUMBER, RANK va DENSE_RANK
Qisqacha: Window funksiya (oyna funksiyasi) — qatorlarni guruhlab "yeb qo'ymasdan", har qatorga qo'shni qatorlar asosida hisoblangan qiymat beradi. Yozilishi:
funksiya() OVER (PARTITION BY ... ORDER BY ...).PARTITION BY— qatorlarni guruhlarga bo'ladi,ORDER BY— guruh ichidagi tartib. Reyting uchun uchta funksiya:ROW_NUMBER(1, 2, 3, 4 — doim noyob),RANK(1, 2, 2, 4 — tenglardan keyin sakraydi),DENSE_RANK(1, 2, 2, 3 — sakramaydi). Window natijasiniWHEREda ishlatib bo'lmaydi — "har guruhdan top-N" CTE orqali filtrlanadi.
Bu darsda
GROUP BYva window funksiya farqini — qatorlar saqlanishini — tushuntirasiz.OVER (),PARTITION BYvaORDER BYbilan oynani belgilaysiz.ROW_NUMBER,RANKvaDENSE_RANKni tenglar bor joyda solishtirasiz.- "Har foydalanuvchining eng yangi 2 vazifasi" ni window funksiya va CTE bilan yechasiz.
- API sahifalash uchun
count(*) OVER ()bilan jami sonni olasiz.
Oldin bilishingiz kerak: CTE (WITH), Agregatlar, GROUP BY va HAVING, ORDER BY, LIMIT va OFFSET.
1. Nega bu kerak?
Agregatlar darsida GROUP BY bilan har foydalanuvchining vazifalarini sanadik. Lekin GROUP BY qatorlarni bitta qatorga yig'adi: Azizning 12 vazifasi o'rniga bitta "Aziz — 12" qatori qoladi. Vazifalarning o'zi yo'qoladi.
Ko'p savollarga esa ikkalasi birga kerak:
- "Har vazifa yonida — egasining jami vazifalari soni" (vazifa qatori ham, guruh soni ham).
- "Foydalanuvchilar reytingi: kim 1-o'rinda, kim 2-da" (har qatorga o'rin raqami).
- "Har foydalanuvchining eng yangi 2 vazifasi" (guruh ichidagi tartib raqami).
Bularni window funksiya hal qiladi. Nomi "oyna" — har qator uchun qo'shni qatorlardan iborat "oyna" ochiladi va funksiya shu oyna bo'yicha hisoblanadi. Qatorning o'zi joyida qoladi. O'xshatish: sinf jurnali. GROUP BY — "sinfning o'rtacha bahosi: 4.2" degan bitta raqam. Window — har o'quvchi yonida uning bahosi ham, sinfdagi o'rni ham yozilgan jurnal.
flowchart LR
V["vazifalar<br/>8 qator"] --> G["GROUP BY egasi_id<br/>2 qator"]
V --> W["count(*) OVER<br/>(PARTITION BY egasi_id)<br/>8 qator"]Diagrammaga qarang: bir xil 8 qator. GROUP BY dan keyin 2 qator qoladi (har egaga bittadan), window'dan keyin — yana 8 ta, har biriga guruh soni qo'shilgan.
2. OVER va PARTITION BY
2.1 Butun natija bo'yicha oyna
Gulnora (6) va Bekzodning (7) vazifalari. Kanon tajribasi (PostgreSQL 18.6, vazifalar holat 23):
SELECT id, egasi_id, matn,
count(*) OVER () AS jami,
count(*) OVER (PARTITION BY egasi_id) AS egasida
FROM vazifalar WHERE egasi_id IN (6, 7) ORDER BY id; id | egasi_id | matn | jami | egasida
----+----------+---------------------------------------------+------+---------
4 | 6 | Somsa uchun xamir tayyorlash #oshxona | 8 | 4
12 | 6 | Buyurtmalarni Telegram'da qabul qilish #ish | 8 | 4
19 | 7 | Universitet kontrakti #to'lov | 8 | 4
22 | 6 | Un va yog' xarid qilish #bozor #oshxona | 8 | 4
25 | 7 | Algoritmlar: 5 ta masala #o'qish | 8 | 4
30 | 6 | Instagram uchun surat olish #ish | 8 | 4
33 | 7 | Noutbukka SSD qo'yish #ta'mir | 8 | 4
39 | 7 | Do'stlar bilan choyxona #dam | 8 | 4
(8 rows)8 qator saqlandi va har biriga ikki son qo'shildi:
| Ifoda | Oyna | Natija |
|---|---|---|
count(*) OVER () |
bo'sh qavs — butun natija | 8 |
count(*) OVER (PARTITION BY egasi_id) |
shu qator egasining qatorlari | 4 |
count, sum, avg kabi oddiy agregatlar OVER bilan window funksiyaga aylanadi. PARTITION BY (bo'lim bo'yicha) — oynani guruhlarga bo'ladi, GROUP BY ga o'xshaydi, lekin qatorlarni birlashtirmaydi. Bu yerda ikkala egada 4 tadan vazifa bor, shuning uchun hamma joyda 4.
Muhim: oyna WHERE dan keyingi qatorlarni ko'radi. jami = 8 — butun jadvalning 47 tasi emas, WHERE egasi_id IN (6, 7) dan o'tgan 8 tasi.
Tekshirib ko'ring: shu so'rovga
round(100.0 / count(*) OVER (PARTITION BY egasi_id))ustunini qo'shsak, nima chiqadi?
Javob
Har qatorda 25 — har vazifa o'z egasining vazifalaridagi ulushi (100 / 4). Window funksiya natijasi oddiy ifodada ishlatilishi mumkin: unga qo'shish, bo'lish, round qilish — hammasi ruxsat.
2.2 Agregat ustidan oyna: ulush
Window funksiya GROUP BY dan keyin ishlaydi. Demak guruhlangan natija ustida ham oyna ochish mumkin. Har foydalanuvchining hamma vazifalardagi ulushi (biz ishga tushirdik, PG 17.6):
SELECT egasi_id, count(*) AS soni,
sum(count(*)) OVER () AS hammasi,
round(100.0 * count(*) / sum(count(*)) OVER (), 1) AS ulush
FROM vazifalar
GROUP BY egasi_id ORDER BY egasi_id; egasi_id | soni | hammasi | ulush
----------+------+---------+-------
1 | 12 | 47 | 25.5
2 | 6 | 47 | 12.8
3 | 6 | 47 | 12.8
4 | 9 | 47 | 19.1
5 | 6 | 47 | 12.8
6 | 4 | 47 | 8.5
7 | 4 | 47 | 8.5
(7 rows)sum(count(*)) OVER () birinchi qarashda g'alati ko'rinadi — agregat ichida agregat. Uni ichkaridan o'qing. count(*) — har guruhning soni: 12, 6, 6, 9, 6, 4, 4. sum(...) OVER () — shu yettita sonning yig'indisi. U har qatorga yozildi: 47. Subquery va LATERAL darsida avg(count(*)) xato bergan edi — chunki u yerda OVER yo'q edi. OVER qo'shilsa, tashqi sum endi oddiy agregat emas, window funksiya bo'ladi va guruhlashdan keyin ishlaydi.
Bu naqsh hisobotlarda juda ko'p: "har kategoriyaning savdodagi ulushi", "har shaharning foydalanuvchilardagi foizi". Ikki so'rov va JavaScript'da bo'lish o'rniga — bitta so'rov.
3. Reyting funksiyalari
3.1 Uch funksiya, tenglar bilan
Foydalanuvchilar vazifalar soni bo'yicha. Bir xil sonlilar (tenglar) bor — uchta odamda 6 tadan, ikkitasida 4 tadan:
SELECT f.ism, count(v.id) AS soni,
row_number() OVER (ORDER BY count(v.id) DESC) AS row_number,
rank() OVER (ORDER BY count(v.id) DESC) AS rank,
dense_rank() OVER (ORDER BY count(v.id) DESC) AS dense_rank
FROM foydalanuvchilar f LEFT JOIN vazifalar v ON v.egasi_id = f.id
GROUP BY f.id ORDER BY soni DESC, f.ism; ism | soni | row_number | rank | dense_rank
--------------------+------+------------+------+------------
Aziz Karimov | 12 | 1 | 1 | 1
Madina Yusupova | 9 | 2 | 2 | 2
Dilnoza Rahimova | 6 | 5 | 3 | 3
Javohir Olimov | 6 | 4 | 3 | 3
Sardor Tursunov | 6 | 3 | 3 | 3
Bekzod Nazarov | 4 | 6 | 6 | 4
Gulnora Saidova | 4 | 7 | 6 | 4
Shahnoza Ergasheva | 0 | 8 | 8 | 5
(8 rows)Kanon tajribasi. Window ichidagi ORDER BY — reyting tartibi. Uch ustunni solishtiring:
| Funksiya | Tenglar (6, 6, 6) | Keyingisi (4) |
|---|---|---|
ROW_NUMBER |
3, 4, 5 — har biri boshqa | 6 |
RANK |
3, 3, 3 — bir xil | 6 — "sakraydi" |
DENSE_RANK |
3, 3, 3 — bir xil | 4 — ketma-ket |
RANK sport musobaqasidagidek: ikki kishi 2-o'rinni bo'lishsa, keyingisi 4-o'rin (3-o'rin yo'q). DENSE_RANK — "zich": o'rinlar teshiksiz. ROW_NUMBER — tenglik bilan ishi yo'q, shunchaki tartib raqami.
Diqqat qiling: GROUP BY bilan window birga ishladi. Window funksiyalar guruhlashdan keyin hisoblanadi, shuning uchun OVER (ORDER BY count(v.id)) ruxsat — agregat natijasi bo'yicha tartiblash.
- Azizrank 112 ta
- Madinarank 29 ta
- Dilnozarank 36 ta
- Javohirrank 36 ta
- Sardorrank 36 ta
- Bekzodrank 64 ta
- Gulnorarank 64 ta
Manba: vazifalar namuna bazasi (holat 23), kanon so'rovi, PostgreSQL 18.6
Grafikda uchta teng ustunga va ulardan keyingi "6-o'rin" ga qarang — RANK ning sakrashi shu. Shahnoza (0 ta) grafikda yo'q — telefon ekraniga 7 ta ustun sig'adi.
3.2 ROW_NUMBER tenglarda tasodifiy
Jadvalda bir nozik joy bor: Dilnoza 5, Javohir 4, Sardor 3 oldi. Nega aynan shunday? Hech qanday sabab yo'q. Oyna ORDER BY count(v.id) DESC bo'yicha tartiblaydi, uchalasining soni bir xil — ular orasidagi tartib kafolatlanmagan. Boshqa Postgres versiyasida yoki ma'lumot boshqacha joylashganda raqamlar almashishi mumkin.
Barqaror raqam uchun oyna tartibiga noyob ustun qo'shing (biz ishga tushirdik, PG 17.6):
SELECT f.ism, count(v.id) AS soni,
row_number() OVER (ORDER BY count(v.id) DESC, f.id) AS rn
FROM foydalanuvchilar f
LEFT JOIN vazifalar v ON v.egasi_id = f.id
GROUP BY f.id ORDER BY rn; ism | soni | rn
--------------------+------+----
Aziz Karimov | 12 | 1
Madina Yusupova | 9 | 2
Dilnoza Rahimova | 6 | 3
Sardor Tursunov | 6 | 4
Javohir Olimov | 6 | 5
Gulnora Saidova | 4 | 6
Bekzod Nazarov | 4 | 7
Shahnoza Ergasheva | 0 | 8
(8 rows)Endi tenglar f.id bo'yicha: Dilnoza (2), Sardor (3), Javohir (5). Bu ORDER BY, LIMIT va OFFSET darsidagi "barqaror tartib" qoidasining o'zi — har qanday tartibda oxiriga noyob kalit.
Tekshirib ko'ring: olimpiadada natijalar 95, 90, 90, 85. Qaysi funksiya "85 ball — 3-o'rin" deydi, qaysi biri "4-o'rin"?
Javob
DENSE_RANK — 3-o'rin (1, 2, 2, 3). RANK — 4-o'rin (1, 2, 2, 4): oldinda uch kishi bor. Ko'p rasmiy musobaqalar RANK mantiqidan foydalanadi. ROW_NUMBER esa 4 beradi, lekin 90 balllilarga 2 va 3 ni tasodifiy taqsimlaydi.
4. Har guruhdan top-N
4.1 PARTITION BY + ROW_NUMBER
PARTITION BY bilan raqamlash har guruhda 1 dan qayta boshlanadi. Gulnoraning vazifalari yaratilish tartibida (biz ishga tushirdik, PG 17.6):
SELECT id, matn,
row_number() OVER (ORDER BY yaratilgan_vaqt) AS n
FROM vazifalar WHERE egasi_id = 6 ORDER BY n; id | matn | n
----+---------------------------------------------+---
4 | Somsa uchun xamir tayyorlash #oshxona | 1
12 | Buyurtmalarni Telegram'da qabul qilish #ish | 2
22 | Un va yog' xarid qilish #bozor #oshxona | 3
30 | Instagram uchun surat olish #ish | 4
(4 rows)Endi "har foydalanuvchining eng yangi 2 vazifasi" — PARTITION BY egasi_id, yangidan eskiga raqamlab, n <= 2 ni olish kerak. Birinchi urinish:
SELECT egasi_id, matn,
row_number() OVER (PARTITION BY egasi_id
ORDER BY yaratilgan_vaqt DESC) AS n
FROM vazifalar WHERE n <= 2;ERROR: column "n" does not exist
LINE 4: FROM vazifalar WHERE n <= 2;
^Tarjimasi: "n ustuni mavjud emas". Sababi — bajarilish tartibi:
| Tartib | Band | n bormi? |
|---|---|---|
| 1 | FROM, WHERE |
hali yo'q |
| 2 | GROUP BY, HAVING |
hali yo'q |
| 3 | window funksiyalar | shu yerda hisoblanadi |
| 4 | SELECT ro'yxati, ORDER BY, LIMIT |
bor |
WHERE window'dan oldin ishlaydi — u n ni ko'ra olmaydi. HAVING ham shunday: Postgres window functions are not allowed in HAVING deydi. SQL tili darsida WHERE da SELECT taxallusi ishlamasligini ko'rgan edik — bu ham o'sha qoida.
4.2 Yechim: CTE bilan filtrlash
Window natijasini avval CTE'da hisoblaymiz, keyin tashqarida filtrlaymiz. Natijada n ustuniga qarang: har egada faqat 1 va 2 qolishi kerak.
WITH tartiblangan AS (
SELECT egasi_id, matn, yaratilgan_vaqt,
row_number() OVER (PARTITION BY egasi_id
ORDER BY yaratilgan_vaqt DESC, id DESC) AS n
FROM vazifalar
)
SELECT egasi_id, n, matn FROM tartiblangan
WHERE n <= 2 ORDER BY egasi_id, n; egasi_id | n | matn
----------+---+-------------------------------------------------
1 | 1 | Velosiped zanjirini moylash
1 | 2 | SQL JOIN mashqlarini yechish #o'qish
2 | 1 | Rezyume yangilash #ish
2 | 2 | Gaz to'lovi #to'lov
3 | 1 | Futbol: payshanba 20:00 #sport
3 | 2 | Soliq hisobotini topshirish #ish #to'lov
4 | 1 | Uyga qo'ng'iroq qilish #oila
4 | 2 | Python laboratoriya ishi #o'qish
5 | 1 | Sport zalga yozilish #sport
5 | 2 | Yangi xodimga kirish huquqi berish #ish #server
6 | 1 | Instagram uchun surat olish #ish
6 | 2 | Un va yog' xarid qilish #bozor #oshxona
7 | 1 | Do'stlar bilan choyxona #dam
7 | 2 | Noutbukka SSD qo'yish #ta'mir
(14 rows)Kanon tajribasi. Natija Subquery va LATERAL darsidagi LATERAL yechimi bilan bir xil. Ikki usulni solishtiramiz:
| Usul | Kuchli tomoni |
|---|---|
LATERAL ... LIMIT 2 |
har guruh uchun indeks bilan faqat 2 qatorni o'qiydi — katta jadvalda tez |
row_number() + CTE |
o'qishga oson, RANK bilan tenglarni ham qo'shish mumkin |
4.3 RANK bilan: tenglar ham kirsin
"Har foydalanuvchining eng muhim bajarilmagan vazifasi". Ustuvorligi bir xil bo'lgan ikkita vazifa bo'lsa — ROW_NUMBER bittasini tasodifiy tanlaydi. RANK esa ikkalasiga 1 beradi:
WITH r AS (
SELECT egasi_id, matn, ustuvorlik,
rank() OVER (PARTITION BY egasi_id
ORDER BY ustuvorlik DESC) AS o
FROM vazifalar WHERE NOT bajarildi
)
SELECT egasi_id, ustuvorlik, matn FROM r
WHERE o = 1 ORDER BY egasi_id, matn; egasi_id | ustuvorlik | matn
----------+------------+--------------------------------------------------
1 | yuqori | JavaScript kursining 26-qismini boshlash #o'qish
1 | yuqori | Oshxonadagi jo'mrakni almashtirish #ta'mir #uy
2 | yuqori | IELTS writing task 2 mashqi #ingliz
3 | yuqori | Mashinani texnik ko'rikka olib borish #mashina
3 | yuqori | Soliq hisobotini topshirish #ish #to'lov
4 | yuqori | Kurs ishi: ma'lumotlar bazasi bo'limi #o'qish
5 | yuqori | Server zaxirasini tekshirish #ish #server
6 | yuqori | Buyurtmalarni Telegram'da qabul qilish #ish
7 | yuqori | Universitet kontrakti #to'lov
(9 rows)Aziz va Sardorda ikkitadan yuqori vazifa bor — ikkalasi ham chiqdi. ORDER BY ustuvorlik DESC enum tartibida ishlaydi: yuqori > orta > past (ALTER TABLE darsidagi ustuvorlik turi). Kanon so'rovida ichki SELECT da muddat ustuni ham bor edi — ishlatilmagani uchun olib tashladik.
5. Oyna tartibi va so'rov tartibi
Window ichidagi ORDER BY va so'rov oxiridagi ORDER BY — ikki mustaqil narsa:
SELECT id, row_number() OVER (ORDER BY id DESC) AS n
FROM vazifalar WHERE egasi_id = 7 ORDER BY id; id | n
----+---
19 | 4
25 | 3
33 | 2
39 | 1
(4 rows)Raqamlar teskari tartibda berildi (eng katta id — 1), natija esa id o'sishi bo'yicha chiqdi. Window'ning ORDER BY i faqat raqam qanday berilishini belgilaydi. Ekrandagi tartib — faqat tashqi ORDER BY dan. Tashqi ORDER BY siz natija tartibi kafolatlanmagan, garchi ko'pincha window tartibiga o'xshab chiqsa ham.
6. Ko'p uchraydigan xatolar
6.1 column "n" does not exist
Window natijasi WHERE da. CTE yoki FROM ichidagi so'rov bilan o'rab, tashqarida filtrlang.
6.2 window functions are not allowed in HAVING
Xuddi shu sabab: HAVING ham window'dan oldin. GROUP BY + window → CTE → tashqi WHERE.
6.3 ROW_NUMBER har safar boshqacha
Oyna tartibida tenglar bor. Oxiriga noyob ustun (id) qo'shing.
6.4 RANK kutilgandan ko'p qator berdi
WHERE o = 1 da tenglar hammasi keladi. Bitta qator kerak bo'lsa — ROW_NUMBER va oyna tartibi oxirida tenglikni ajratuvchi noyob ustun (id).
6.5 Window GROUP BY dagi xato
GROUP BY f.id li so'rovda oyna tartibi guruhlanmagan ustun bo'yicha (biz ishga tushirdik, PG 17.6):
SELECT f.ism, row_number() OVER (ORDER BY v.yaratilgan_vaqt) AS n
FROM foydalanuvchilar f JOIN vazifalar v ON v.egasi_id = f.id
GROUP BY f.id;ERROR: column "v.yaratilgan_vaqt" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT f.ism, row_number() OVER (ORDER BY v.yaratilgan_vaqt)...
^Window guruhlashdan keyin ishlaydi va faqat guruhlangan ustunlar yoki agregatlarni ko'radi. Bitta foydalanuvchida 12 ta yaratilgan_vaqt bor — qaysi biri bo'yicha tartiblash noma'lum. Agregat bilan yozing: OVER (ORDER BY min(v.yaratilgan_vaqt)) — "birinchi vazifasi qachon".
7. Mashqlar
Mashq fayllari kurs/mashqlar/26/28-window-rank/ da. Hammasi vazifalar holat 23 da.
1-mashq (oson): vazifa ulushi
Bekzodning (egasi_id = 7) vazifalari: id, matn va jami — uning hamma vazifalari soni har qatorda. Yordam: count(*) OVER ().
Yechim
SELECT id, matn, count(*) OVER () AS jami
FROM vazifalar WHERE egasi_id = 7 ORDER BY id; id | matn | jami
----+----------------------------------+------
19 | Universitet kontrakti #to'lov | 4
25 | Algoritmlar: 5 ta masala #o'qish | 4
33 | Noutbukka SSD qo'yish #ta'mir | 4
39 | Do'stlar bilan choyxona #dam | 4
(4 rows)Oyna WHERE dan o'tgan 4 qatordan iborat — jami 4.
2-mashq (o'rta): har kimning birinchi vazifasi
Har foydalanuvchining eng birinchi (eng eski) vazifasini chiqaring: ism, matn. Bir xil vaqt bo'lsa — kichik id. Yordam: row_number() OVER (PARTITION BY egasi_id ORDER BY yaratilgan_vaqt, id) CTE'da, tashqarida n = 1 va foydalanuvchilar bilan JOIN.
Yechim
WITH t AS (
SELECT egasi_id, matn, yaratilgan_vaqt,
row_number() OVER (PARTITION BY egasi_id
ORDER BY yaratilgan_vaqt, id) AS n
FROM vazifalar
)
SELECT f.ism, t.matn
FROM t JOIN foydalanuvchilar f ON f.id = t.egasi_id
WHERE t.n = 1 ORDER BY f.id; ism | matn
------------------+--------------------------------------------------
Aziz Karimov | JavaScript kursining 26-qismini boshlash #o'qish
Dilnoza Rahimova | Ingliz tili: 20 ta yangi so'z #o'qish #ingliz
Sardor Tursunov | Mashinani texnik ko'rikka olib borish #mashina
Madina Yusupova | Kurs ishi: ma'lumotlar bazasi bo'limi #o'qish
Javohir Olimov | Mijozga taklifnoma yuborish #ish
Gulnora Saidova | Somsa uchun xamir tayyorlash #oshxona
Bekzod Nazarov | Universitet kontrakti #to'lov
(7 rows)Shahnoza chiqmadi — unda vazifa yo'q, CTE'da uning qatori ham yo'q. Uni ham ko'rsatish kerak bo'lsa — foydalanuvchilar LEFT JOIN t ON ... AND t.n = 1.
3-mashq (qiyin): teglar reytingi
Teglarni nomi bo'yicha (hamma foydalanuvchilar birga) nechta vazifada ishlatilgani bo'yicha reytinglang. DENSE_RANK bilan birinchi 3 o'rinni chiqaring: orin, soni, shu o'rindagi teglar (bo'shliq bilan). Yordam: 1) CTE — teglar JOIN vazifa_teglari, GROUP BY t.nom; 2) CTE — dense_rank() OVER (ORDER BY soni DESC); 3) orin <= 3, GROUP BY orin, soni va string_agg.
Yechim
WITH teg_soni AS (
SELECT t.nom, count(*) AS soni
FROM teglar t JOIN vazifa_teglari vt ON vt.teg_id = t.id
GROUP BY t.nom
), r AS (
SELECT nom, soni, dense_rank() OVER (ORDER BY soni DESC) AS orin
FROM teg_soni
)
SELECT orin, soni, string_agg(nom, ' ' ORDER BY nom) AS teglar
FROM r WHERE orin <= 3 GROUP BY orin, soni ORDER BY orin; orin | soni | teglar
------+------+---------
1 | 9 | #ish
2 | 8 | #o'qish
3 | 5 | #to'lov
(3 rows)#ish — eng ommabop teg (9 vazifa). Bu ma'lumotda tenglar yo'q, shuning uchun har o'rinda bitta teg. DENSE_RANK tanlandi: tenglar bo'lsa ham 3 ta o'rin ko'rinadi (RANK da 3-o'rin "sakrab" ketishi mumkin edi).
4-mashq: SQL qadami — sahifa va jami bitta so'rovda
CTE (WITH) darsida "ro'yxat + jami" ni CTE bilan yozgan edik. Endi window bilan: Azizning bajarilmagan vazifalaridan eng yangi 3 tasi va har qatorda jami soni. Sxema o'zgarmaydi — commit yo'q. Yordam: count(*) OVER () — LIMIT dan oldin hisoblanadi.
Yechim
SELECT id, matn, count(*) OVER () AS jami
FROM vazifalar
WHERE egasi_id = 1 AND NOT bajarildi
ORDER BY yaratilgan_vaqt DESC, id DESC
LIMIT 3; id | matn | jami
----+------------------------------------------------+------
38 | Velosiped zanjirini moylash | 7
34 | SQL JOIN mashqlarini yechish #o'qish | 7
23 | Oshxonadagi jo'mrakni almashtirish #ta'mir #uy | 7
(3 rows)jami = 7 — LIMIT 3 emas, filtrlangan hamma qatorlar. Window LIMIT dan oldin hisoblanadi (bajarilish tartibi jadvalini eslang). Bitta tuzoq: sahifa bo'sh bo'lsa (masalan OFFSET 10), qator ham, jami ham kelmaydi — API "0 ta" deb noto'g'ri javob berishi mumkin. Shunda alohida count(*) so'rovi kerak. Katta jadvalda OFFSET ning o'zi ham sekin — Keyset pagination darsida.
8. Real ishda
- Reytinglar: eng ko'p sotilgan mahsulotlar, haftaning eng faol o'quvchilari, o'yin peshqadamlari jadvali (leaderboard).
- Top-N har guruhda: har kategoriyaning eng arzon 3 mahsuloti, har bo'limning eng yaxshi xodimi, har chatning oxirgi xabari.
- Takrorlarni tozalash:
row_number() OVER (PARTITION BY email ORDER BY id)—n > 1bo'lgan qatorlar — takror nusxalar, ularni o'chirish mumkin. - API:
count(*) OVER ()— sahifa va jami sonni bitta so'rovda. - Intervyu: "RANK va DENSE_RANK farqi", "har bo'limning eng yuqori maoshli 2 xodimi" — SQL intervyularining eng mashhur savollari.
Xulosa
- Window funksiya qatorlarni saqlaydi va har biriga oyna bo'yicha hisoblangan qiymat qo'shadi:
f() OVER (PARTITION BY ... ORDER BY ...). OVER ()— butun natija,PARTITION BY— guruhlar; oynaWHEREvaGROUP BYdan keyingi qatorlarni ko'radi.ROW_NUMBER— noyob raqam,RANK— tenglardan keyin sakraydi,DENSE_RANK— sakramaydi.- Window natijasi
WHERE/HAVINGda ishlamaydi — CTE bilan o'rab, tashqarida filtrlang. - Tenglarda
ROW_NUMBERtasodifiy — oyna tartibiga noyob ustun qo'shing.
Keyingi dars: Window funksiyalar 2: qiymat va frame'lar — LAG, LEAD, yig'ilib boruvchi jami va sirpanuvchi o'rtacha.
Manbalar
- PostgreSQL 18 hujjati — Window Functions (tutorial): https://www.postgresql.org/docs/18/tutorial-window.html
- PostgreSQL 18 hujjati — Window Functions (ro'yxat): https://www.postgresql.org/docs/18/functions-window.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!