9-bo‘lim
Oyna funksiyalari
OVER nima qiladi, ROW_NUMBER va RANK farqi, PARTITION BY, LAG va LEAD, yig'ilib boruvchi jami va oyna ramkasi.
Ushbu bo‘lim mundarijasi
Agregat funksiya qatorlarni birlashtiradi: o'nta qatordan bitta natija chiqadi. Ba'zan esa hisob kerak, lekin qatorlar ham qolishi kerak.
Aynan shu vazifani oyna funksiyalari bajaradi.
CREATE TABLE sotuv (
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
filial text NOT NULL,
oy int NOT NULL,
summa numeric(10,2) NOT NULL
);
INSERT INTO sotuv (filial, oy, summa) VALUES
('Toshkent', 1, 450000), ('Toshkent', 2, 520000), ('Toshkent', 3, 380000),
('Namangan', 1, 310000), ('Namangan', 2, 275000), ('Namangan', 3, 410000),
('Samarqand',1, 380000), ('Samarqand',2, 380000), ('Samarqand',3, 295000);
GROUP BY va OVER farqi #
Farqni yonma-yon ko'ramiz:
SELECT filial, oy, summa,
sum(summa) OVER (PARTITION BY filial) AS filial_jami,
sum(summa) OVER () AS umumiy_jami
FROM sotuv
ORDER BY filial, oy;
filial | oy | summa | filial_jami | umumiy_jami
-----------+----+-----------+-------------+-------------
Namangan | 1 | 310000.00 | 995000.00 | 3400000.00
Namangan | 2 | 275000.00 | 995000.00 | 3400000.00
Namangan | 3 | 410000.00 | 995000.00 | 3400000.00
Samarqand | 1 | 380000.00 | 1055000.00 | 3400000.00
Samarqand | 2 | 380000.00 | 1055000.00 | 3400000.00
Samarqand | 3 | 295000.00 | 1055000.00 | 3400000.00
Toshkent | 1 | 450000.00 | 1350000.00 | 3400000.00
Toshkent | 2 | 520000.00 | 1350000.00 | 3400000.00
Toshkent | 3 | 380000.00 | 1350000.00 | 3400000.00
(9 rows)
Har qator o'z joyida qoldi, lekin yoniga ikkita yangi ustun qo'shildi.
| Yozuv | Ma'nosi |
|---|---|
OVER () | Butun natija - bitta katta oyna |
OVER (PARTITION BY filial) | Har filial - alohida oyna |
PARTITION BY - bu GROUP BY ga o'xshaydi, lekin qatorlarni
yo'qotmaydi.
Tartib raqami va reyting #
Uchta o'xshash funksiya bor va ularning farqi teng qiymatlarda ko'rinadi:
SELECT filial, summa,
row_number() OVER (ORDER BY summa DESC) AS raqam,
rank() OVER (ORDER BY summa DESC) AS oddiy_reyting,
dense_rank() OVER (ORDER BY summa DESC) AS zich_reyting
FROM sotuv
ORDER BY summa DESC;
filial | summa | raqam | oddiy_reyting | zich_reyting
-----------+-----------+-------+---------------+--------------
Toshkent | 520000.00 | 1 | 1 | 1
Toshkent | 450000.00 | 2 | 2 | 2
Namangan | 410000.00 | 3 | 3 | 3
Samarqand | 380000.00 | 4 | 4 | 4
Toshkent | 380000.00 | 5 | 4 | 4
Samarqand | 380000.00 | 6 | 4 | 4
Namangan | 310000.00 | 7 | 7 | 5
Samarqand | 295000.00 | 8 | 8 | 6
Namangan | 275000.00 | 9 | 9 | 7
(9 rows)
Natijada ikkita 380000.00 bor - ular teng. Mana shu yerda
farq ko'rinadi:
| Funksiya | Teng qiymatlarda | Keyingi raqam |
|---|---|---|
row_number() | Har xil raqam beradi | ketma-ket |
rank() | Bir xil raqam beradi | sakraydi |
dense_rank() | Bir xil raqam beradi | sakramaydi |
Sport misolida: ikki chempion birinchi o'rinni bo'lishsa,
keyingisi rank() da uchinchi, dense_rank() da esa
ikkinchi bo'ladi.
row_number() teng qiymatlarda qaysi qatorga qaysi raqam
tushishini kafolatlamaydi - barqaror natija kerak bo'lsa,
ORDER BY ga qo'shimcha ustun qo'shing.
PARTITION BY bilan birga ishlatilsa, har guruhda alohida
reyting chiqadi:
SELECT filial, oy, summa,
row_number() OVER (PARTITION BY filial ORDER BY summa DESC) AS orin
FROM sotuv
ORDER BY filial, orin;
filial | oy | summa | orin
-----------+----+-----------+------
Namangan | 3 | 410000.00 | 1
Namangan | 1 | 310000.00 | 2
Namangan | 2 | 275000.00 | 3
Samarqand | 2 | 380000.00 | 1
Samarqand | 1 | 380000.00 | 2
Samarqand | 3 | 295000.00 | 3
Toshkent | 2 | 520000.00 | 1
Toshkent | 1 | 450000.00 | 2
Toshkent | 3 | 380000.00 | 3
(9 rows)
Bu 7-bo'limdagi LATERAL vazifasining ikkinchi yechimi. Har
filialning eng yaxshi oyini olish uchun endi orin = 1 deb
filtrlash kifoya.
WHERE da ishlatib bo'lmaydiBu mantiqiy tuzoq:
WHERE row_number() OVER (...) = 1
Bunday yozuv xato beradi. Sabab - bajarilish tartibi:
WHERE oyna funksiyalaridan oldin ishlaydi, ya'ni o'sha
paytda tartib raqami hali hisoblanmagan.
Yechim - so'rovni ichki qilib o'rash:
SELECT * FROM (
SELECT filial, summa,
row_number() OVER (PARTITION BY filial
ORDER BY summa DESC) AS orin
FROM sotuv
) t WHERE orin = 1;
10-bo'limdagi WITH yozuvi bu naqshni ancha o'qiluvchan
qiladi.
LAG va LEAD - qo'shni qatorga qarash #
Oldingi yoki keyingi qator bilan solishtirish - hisobotlarda eng ko'p uchraydigan vazifa:
SELECT filial, oy, summa,
lag(summa) OVER (PARTITION BY filial ORDER BY oy) AS oldingi_oy,
summa - lag(summa) OVER (PARTITION BY filial ORDER BY oy) AS ozgarish
FROM sotuv
ORDER BY filial, oy;
filial | oy | summa | oldingi_oy | ozgarish
-----------+----+-----------+------------+------------
Namangan | 1 | 310000.00 | |
Namangan | 2 | 275000.00 | 310000.00 | -35000.00
Namangan | 3 | 410000.00 | 275000.00 | 135000.00
Samarqand | 1 | 380000.00 | |
Samarqand | 2 | 380000.00 | 380000.00 | 0.00
Samarqand | 3 | 295000.00 | 380000.00 | -85000.00
Toshkent | 1 | 450000.00 | |
Toshkent | 2 | 520000.00 | 450000.00 | 70000.00
Toshkent | 3 | 380000.00 | 520000.00 | -140000.00
(9 rows)
Birinchi oyda oldingi_oy bo'sh - undan oldin qator yo'q.
Buni lag(summa, 1, 0) bilan nolga almashtirish mumkin.
lead() esa teskarisi - keyingi qatorga qaraydi.
Yig'ilib boruvchi jami #
Oyna ramkasi har qator uchun qaysi qatorlar hisobga olinishini belgilaydi:
SELECT filial, oy, summa,
sum(summa) OVER (
PARTITION BY filial ORDER BY oy
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS yigilgan
FROM sotuv
ORDER BY filial, oy;
filial | oy | summa | yigilgan
-----------+----+-----------+------------
Namangan | 1 | 310000.00 | 310000.00
Namangan | 2 | 275000.00 | 585000.00
Namangan | 3 | 410000.00 | 995000.00
Samarqand | 1 | 380000.00 | 380000.00
Samarqand | 2 | 380000.00 | 760000.00
Samarqand | 3 | 295000.00 | 1055000.00
Toshkent | 1 | 450000.00 | 450000.00
Toshkent | 2 | 520000.00 | 970000.00
Toshkent | 3 | 380000.00 | 1350000.00
(9 rows)
UNBOUNDED PRECEDING AND CURRENT ROW - "boshidan shu
qatorgacha". Shuning uchun har qatorda jami o'sib boradi.
ORDER BY oyna ichida ramkani o'zgartiradiBu nozik, lekin muhim.
| Yozuv | Standart ramka |
|---|---|
OVER (PARTITION BY x) | Butun bo'lim |
OVER (PARTITION BY x ORDER BY y) | Boshidan joriy qatorgacha |
Ya'ni oyna ichiga ORDER BY qo'shishning o'zi sum() ni
"jami" dan "yig'ilib boruvchi jami" ga aylantiradi.
Ko'p odam buni bilmasdan ORDER BY qo'shib, natija
o'zgarganiga hayron bo'ladi. Ramkani aniq yozsangiz, hech
qanday sir qolmaydi.
O'rtacha bilan solishtirish #
Oyna funksiyalari har qatorni guruh o'rtachasi bilan solishtirishni osonlashtiradi:
SELECT filial, oy, summa,
round(avg(summa) OVER (PARTITION BY filial), 2) AS filial_ortacha,
round(summa - avg(summa) OVER (PARTITION BY filial), 2) AS farq
FROM sotuv
ORDER BY filial, oy;
filial | oy | summa | filial_ortacha | farq
-----------+----+-----------+----------------+-----------
Namangan | 1 | 310000.00 | 331666.67 | -21666.67
Namangan | 2 | 275000.00 | 331666.67 | -56666.67
Namangan | 3 | 410000.00 | 331666.67 | 78333.33
Samarqand | 1 | 380000.00 | 351666.67 | 28333.33
Samarqand | 2 | 380000.00 | 351666.67 | 28333.33
Samarqand | 3 | 295000.00 | 351666.67 | -56666.67
Toshkent | 1 | 450000.00 | 450000.00 | 0.00
Toshkent | 2 | 520000.00 | 450000.00 | 70000.00
Toshkent | 3 | 380000.00 | 450000.00 | -70000.00
(9 rows)
GROUP BY bilan buni qilish uchun jadvalni o'zi bilan
birlashtirish kerak bo'lardi.
OVER ()bilan umumiy jamini har qatorga qo'shing.PARTITION BYqo'shing va farqni kuzating.row_number(),rank()vadense_rank()natijalarini solishtiring.- Teng qiymatlarda ularning farqini yozing.
- Har filialning eng yuqori oyini
orin = 1bilan toping. WHEREga oyna funksiyasini yozib ko'ring - qanday xato chiqdi?lagbilan oldingi oyga nisbatan o'zgarishni hisoblang.lag(summa, 1, 0)yozing - birinchi qator qanday o'zgardi?- Oyna ichiga
ORDER BYqo'shing vasumnatijasi qanday o'zgarishini ko'ring. - Har qatorni filial o'rtachasi bilan solishtiring.
Xulosa #
- Oyna funksiyasi hisoblaydi, lekin qatorlarni yo'qotmaydi.
OVER ()- butun natija,OVER (PARTITION BY x)- har guruh alohida.row_number()teng qiymatlarga har xil,rank()vadense_rank()bir xil raqam beradi.rank()dan keyingi raqam sakraydi,dense_rank()da esa sakramaydi.- Oyna funksiyasini
WHEREda ishlatib bo'lmaydi - u kechroq hisoblanadi. - Yechim: so'rovni ichki qilib o'rash yoki
WITHishlatish. lag()oldingi,lead()keyingi qatorga qaraydi.- Uchinchi argument bilan bo'sh qiymatga standart berish mumkin:
lag(x, 1, 0). - Oyna ichidagi
ORDER BYramkani o'zgartiradi -sumyig'ilib boruvchi bo'lib qoladi. - Ramkani (
ROWS BETWEEN ...) ochiq yozish chalkashlikni yo'q qiladi.
Keyingi bo'limda murakkab so'rovlarni bo'laklarga ajratishni va rekursiv so'rovlarni ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.