IlmHamroh
JavaScript Full-stack/26-qism. SQL va PostgreSQL22/64-dars19 daqiqa
Mundarija (27)

PostgreSQL funksiyalari: CASE, matn, sana va son funksiyalari

Qisqacha: SQL'da SELECT ro'yxati, WHERE va ORDER BY da faqat ustun emas, ifoda yoziladi: arifmetika, CASE WHEN … THEN … END (agar-aks holda), tur o'girish ('42'::int) va yuzlab o'rnatilgan funksiyalar. Matn uchun — lower, trim, concat, regexp_replace, split_part; vaqt uchun — now(), date_trunc, extract, interval, age; son uchun — round, greatest, least. Tuzoqlar: 7 / 2 = 3 (butun bo'linma), || NULL beradi, concat esa NULL ni tashlaydi, now() tranzaksiya ichida o'zgarmaydi.

Bu darsda

  • CASE bilan qiymatga qarab matn yoki son chiqarasiz.
  • :: va CAST bilan tur o'girasiz, butun son bo'linmasi tuzog'ini bilasiz.
  • Matn funksiyalari bilan tozalaysiz, ajratasiz va formatlaysiz.
  • now(), date_trunc, extract, interval, age bilan vaqt hisoblaysiz.
  • round, greatest, least va interval agregatlari bilan statistika chiqarasiz.

Oldin bilishingiz kerak: ALTER TABLE va sxemani o'zgartirish, SELECT va WHERE, NULL mantiqi, PostgreSQL ma'lumot turlari.

1. Nega bu kerak?

ALTER TABLE darsida vazifalarga muddat, bajarilgan_vaqt, ustuvorlik qo'shdik. Endi ilova savollar beradi:

  • Bu vazifa "faol"mi, "muddati o'tgan"mi yoki "bajarilgan"mi?
  • Muddatga qancha vaqt qoldi?
  • Vazifalar o'rtacha qancha vaqtda bajariladi?
  • Telefon raqamini bir xil shaklga keltirish kerak.

Har birini ilovada — JavaScript'da — qilish mumkin: hamma qatorni olib, aylantirib. Lekin 40 ta emas, 2 million qator bo'lsa-chi? "Muddati o'tganlar soni" uchun 2 million qatorni tarmoq orqali tortish — isrof. Baza shu hisobni ma'lumot yonida bajaradi va faqat natijani beradi.

Ikkinchi sabab — bir xillik. "Muddati o'tgan" qoidasi frontend'da, mobil ilovada va hisobotda uch xil yozilsa, ertaga ular farq qilib qoladi. So'rovdagi bitta CASE hammaga bir xil javob beradi.

O'xshatish: kalkulyatorli kassa. Kassir har mahsulot narxini qog'ozga yozib, uyga olib ketib, u yerda qo'shmaydi. Kassa apparati darhol jami, chegirma va qaytimni hisoblaydi. SQL funksiyalari — bazaning kalkulyatori.

2. CASE: agar-aks holda

2.1 Qidiruvli CASE

CASE — JavaScript'dagi if … else if … else ning SQL ko'rinishi, faqat u qiymat qaytaradi. Bu darsdagi misollar holat 21 bazasida. Joriy vaqt har kuni o'zgaradi, shuning uchun kanon "bugun" sifatida qat'iy vaqt oladi: 2026-10-20 12:00+05. Kanon (PostgreSQL 18.6):

sql
SELECT id, matn,
  CASE WHEN bajarildi THEN 'bajarilgan'
       WHEN muddat < '2026-10-20 12:00+05' THEN 'muddati o''tgan'
       WHEN muddat IS NULL THEN 'muddatsiz'
       ELSE 'faol' END AS holat
FROM vazifalar
WHERE egasi_id = 3
ORDER BY id;

Natija:

text
 id |                      matn                      |     holat      
----+------------------------------------------------+----------------
 10 | Mashinani texnik ko'rikka olib borish #mashina | muddati o'tgan
 11 | Moy almashtirish #mashina                      | bajarilgan
 21 | Hovlidagi daraxtlarni oqlash #uy               | bajarilgan
 24 | Do'konga yangi narxlarni kiritish #ish         | muddati o'tgan
 32 | Soliq hisobotini topshirish #ish #to'lov       | faol
 35 | Futbol: payshanba 20:00 #sport                 | bajarilgan
(6 rows)

Bitta qator uchun CASE shunday "yuradi":

flowchart TD
  A["Qator"] --> B{"bajarildi?"}
  B -->|true| R1["'bajarilgan'"]
  B -->|"false"| C{"muddat < bugun?"}
  C -->|true| R2["'muddati o''tgan'"]
  C -->|"false yoki NULL"| D{"muddat IS NULL?"}
  D -->|true| R3["'muddatsiz'"]
  D -->|false| R4["ELSE: 'faol'"]

Diagrammadagi o'rta tarmoqqa qarang: NULL ham false yo'lidan ketadi.

Qoidalar:

  • Shartlar yuqoridan pastga tekshiriladi, birinchi true bo'lgan yutadi. 11-vazifa bajarilgan — keyingi shartlar uning uchun tekshirilmaydi. Tartib muhim: bajarildi birinchi bo'lmasa, muddati o'tgan bajarilgan vazifa "muddati o'tgan" deb chiqardi.
  • ELSE bo'lmasa va hech bir shart mos kelmasa — NULL.
  • Shart NULL bo'lsa — u "mos kelmadi" hisoblanadi (NULL mantiqi). Sardorning muddatsiz vazifasi muddat < … da NULL beradi va keyingi shartga o'tadi.

2.2 Oddiy CASE

Bitta ustunni bir nechta qiymat bilan solishtirsangiz — qisqa shakl. Biz ishga tushirdik (PostgreSQL 17.6, Windows 11):

sql
SELECT ustuvorlik,
  CASE ustuvorlik WHEN 'yuqori' THEN '!!!'
                  WHEN 'orta' THEN '!!'
                  ELSE '!' END AS belgi
FROM vazifalar WHERE id IN (1, 2, 38) ORDER BY id;

Natija:

text
 ustuvorlik | belgi 
------------+-------
 yuqori     | !!!
 orta       | !!
 past       | !
(3 rows)

CASE istalgan joyda ishlaydi: ORDER BY CASE … END (o'z tartibingiz), count(*) FILTER o'rnida sum(CASE WHEN … THEN 1 ELSE 0 END) (boshqa bazalarda), ALTER TABLE darsidagi USING CASE ….

Tekshirib ko'ring: CASE WHEN muddat IS NULL THEN 'muddatsiz' WHEN bajarildi THEN 'bajarilgan' … END — muddatsiz, lekin bajarilgan vazifa nima deb chiqadi?

Javob

"muddatsiz". Birinchi mos shart yutadi, bajarildi tekshirilmaydi ham. Shuning uchun eng muhim holat (bajarildi) birinchi turishi kerak.

3. Tur o'girish

3.1 :: va CAST

ifoda::tur — PostgreSQL'ning qisqa yozuvi, CAST(ifoda AS tur) — SQL standarti. Ma'nosi bir xil. Kanon (PostgreSQL 18.6):

sql
SELECT '42'::int + 1 AS cast1,
       CAST('2026-10-24' AS date) AS cast2,
       7 / 2 AS butun_bolish,
       7 / 2.0 AS kasr,
       7::numeric / 2 AS numeric_bolish;

Natija:

text
 cast1 |   cast2    | butun_bolish |        kasr        |   numeric_bolish   
-------+------------+--------------+--------------------+--------------------
    43 | 2026-10-24 |            3 | 3.5000000000000000 | 3.5000000000000000
(1 row)

Uchinchi ustunga qarang: 7 / 2 = 3. Ikki butun son bo'linsa, natija ham butun — kasr qismi tashlanadi. JavaScript'da 7 / 2 = 3.5 edi — bu yerda tuzoq. Bittasini kasrga aylantiring: 7 / 2.0 yoki 7::numeric / 2. Agregatlar darsidagi foiz hisobida 100.0 * … yozganimiz shu sababdan.

O'girib bo'lmaydigan qiymat — xato. Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT 'qirq ikki'::int;

Natija:

text
ERROR:  invalid input syntax for type integer: "qirq ikki"
LINE 1: SELECT 'qirq ikki'::int;
               ^

Tarjimasi: "integer turi uchun noto'g'ri kiritish: «qirq ikki»". JavaScript'dagi Number("qirq ikki") jim NaN qaytarardi; PostgreSQL esa to'xtatadi. Bu yaxshi: noto'g'ri ma'lumot jim tarqalmaydi.

4. Matn funksiyalari

4.1 Asosiylar

Kanon (PostgreSQL 18.6):

sql
SELECT concat('Aziz', ' ', NULL, 'Karimov') AS concat_null_tashlaydi,
       'Aziz' || ' ' || NULL AS pipe_null_beradi,
       lower('O''ZBEKISTON'), upper('oʻzbek'),
       initcap('aziz karimov'), trim('  salom  ') AS trim,
       substring('Postgres''ni Docker''da' FROM 1 FOR 8)
         AS substring,
       left('JavaScript', 4), length('Toshkent');

Natija:

text
 concat_null_tashlaydi | pipe_null_beradi |    lower    | upper  |   initcap    | trim  | substring | left | length 
-----------------------+------------------+-------------+--------+--------------+-------+-----------+------+--------
 Aziz Karimov          |                  | o'zbekiston | OʻZBEK | Aziz Karimov | salom | Postgres  | Java |      8
(1 row)
Funksiya Ish JavaScript'dagi o'xshashi
Ikki vertikal chiziq (a, b orasida) Ulash; NULL bo'lsa — NULL a + b
concat(a, b, …) Ulash; NULL ni tashlaydi [a, b].join("")
lower, upper Kichik/katta harf toLowerCase()
initcap Har so'z bosh harfi katta —
trim, btrim Chetdagi bo'shliqlar trim()
substring(s FROM i FOR n) Qism (1 dan sanaladi!) slice(i - 1, …)
left(s, n), right(s, n) Boshidan/oxiridan n belgi slice(0, n)
length, char_length Belgilar soni length (taxminan)

Ikki tuzoq:

  • Indeks 1 dan boshlanadi. substring(… FROM 1 FOR 8) — birinchi 8 belgi. JavaScript'da indeks 0 dan.
  • || va NULL. Ikkinchi ustun bo'sh — NULL. Ism yoki familiyasi NULL bo'lgan foydalanuvchi uchun ism || ' ' || familiya butunlay NULL bo'ladi. concat — xavfsizroq.

upper('oʻzbek') → OʻZBEK: ʻ (U+02BB) harf emas, u o'zgarmadi. PostgreSQL ma'lumot turlari darsida ko'rganimizdek, length belgilarni sanaydi, bayt emas. Biz ishga tushirdik (PostgreSQL 17.6): char_length('oʻqish') — 6, octet_length('oʻqish') — 7 (ʻ 2 bayt).

Diqqat: JS "…".length UTF-16 birliklarini sanaydi, char_length — Unicode belgilarni. Emoji'da ular farq qiladi (2 va 1). Frontend "100 belgi" deb tekshirgan matnni baza boshqacha sanashi mumkin — CREATE TABLE darsidagi matn cheklovi bilan bog'liq eslatma.

4.2 Tozalash va ajratish

Kanon (PostgreSQL 18.6):

sql
SELECT regexp_replace('+998 90 123-45-67', '[^0-9]', '', 'g')
         AS faqat_raqam,
       split_part('aziz@example.com', '@', 2) AS domen,
       replace('Bozor #bozor', '#', '') AS replace,
       format('%s ning %s ta vazifasi', 'Aziz', 9) AS format,
       position('#' IN 'Non olish #uy') AS pozitsiya;

Natija:

text
 faqat_raqam  |    domen    |   replace   |         format          | pozitsiya 
--------------+-------------+-------------+-------------------------+-----------
 998901234567 | example.com | Bozor bozor | Aziz ning 9 ta vazifasi |        11
(1 row)
  • regexp_replace(matn, regex, almashtirish, 'g') — regex bo'yicha almashtirish. 'g' — hammasini (JS'dagi /…/g flagi). [^0-9] — "raqam bo'lmagan har belgi". Natija — faqat raqamlar: telefonni bir xil shaklda saqlash uchun.
  • split_part(matn, ajratgich, n) — ajratib, n-bo'lakni oladi. JS'dagi split("@")[1] (lekin raqam 1 dan).
  • replace — oddiy (regex'siz) almashtirish.
  • format — shablon; %s — qiymat, %I — nom, %L — qiymat tirnoq bilan. Oxirgi ikkitasi dinamik SQL uchun (Parametrli so'rovlar).
  • position — qism qayerda (1 dan; topilmasa 0).

4.3 Funksiya WHERE'da

Funksiyalar WHERE da ham ishlaydi. Biz ishga tushirdik (PostgreSQL 17.6):

sql
SELECT id, matn FROM vazifalar
WHERE lower(matn) LIKE '%bozor%' AND egasi_id = 1;

Natija:

text
 id |                   matn                   
----+------------------------------------------
  8 | Bozordan kartoshka va piyoz olish #bozor
(1 row)

Bu ILIKE ning boshqa bazalardagi ekvivalenti. Lekin bir narsa bor: ustun funksiyaga o'ralsa (lower(matn)), oddiy indeks matn bo'yicha ishlamaydi. Uning uchun ifoda indeksi kerak — CREATE INDEX … (lower(matn)). Aynan shunday indeks bizda bor: foydalanuvchilar_email_key — lower(email) ustida (CREATE TABLE). Batafsil — Partial, expression va covering indekslar.

Tekshirib ko'ring: split_part('2026-10-24', '-', 2) va substring('Toshkent' FROM 2 FOR 3) nima qaytaradi?

Javob

'10' va 'osh'. Ikkalasida ham sanash 1 dan: ikkinchi bo'lak — oy; ikkinchi belgidan boshlab 3 ta — o, s, h.

5. Sana va vaqt funksiyalari

5.1 Kesish, ajratish, qo'shish

Kanon (PostgreSQL 18.6, sessiya zonasi UTC):

sql
SELECT date_trunc('month', '2026-10-24 15:40+05'::timestamptz)
         AS oy_boshi,
       extract(dow FROM '2026-10-24'::date) AS hafta_kuni,
       extract(epoch FROM interval '1 day') AS soniya,
       age('2026-10-24'::date, '2007-03-15'::date) AS yosh,
       '2026-10-24 15:40'::timestamp + interval '90 minutes'
         AS qoshildi;

Natija:

text
        oy_boshi        | hafta_kuni |    soniya    |          yosh          |      qoshildi       
------------------------+------------+--------------+------------------------+---------------------
 2026-10-01 00:00:00+00 |          6 | 86400.000000 | 19 years 7 mons 9 days | 2026-10-24 17:10:00
(1 row)
Funksiya Ish
date_trunc('month', t) Oy boshigacha "kesish" ('day', 'week', 'hour' ham)
extract(qism FROM t) Qismni son qilib olish: year, month, day, hour, dow (hafta kuni, 0 — yakshanba)
extract(epoch FROM …) Soniyalar soni
age(a, b) Ikki sana farqi — yil, oy, kun bilan
t + interval '90 minutes' Vaqt qo'shish/ayirish

2026-10-24 — shanba, shuning uchun dow = 6. age — yoshni hisoblash uchun qulay: "19 yil 7 oy 9 kun".

date_trunc Agregatlar darsida haftalik guruhlashda ishlatilgan edi. oy_boshi dagi 00:00:00+00 ga qarang — oy boshi UTC'da hisoblandi. Toshkentning oy boshi esa 5 soat oldin. Bu muammo va uning yechimi (date_trunc(…, 'Asia/Tashkent'), AT TIME ZONE) — SQL'da sana va vaqt zonalari darsining asosiy mavzusi.

Oy oxiri arifmetikasi — biz ishga tushirdik (PostgreSQL 17.6): '2026-01-31'::date + interval '1 month' → 2026-02-28 00:00:00. 31-fevral yo'q, PostgreSQL oyning oxirgi kuniga tushiradi.

5.2 Vaqt ayirmasi — interval

Ikki timestamptz ayirmasi — interval. Kanon (PostgreSQL 18.6) — "bugun" (qat'iy) dan keyingi eng yaqin 5 ta muddat:

sql
SELECT id, matn, muddat - '2026-10-20 12:00+05' AS qolgan_vaqt
FROM vazifalar
WHERE muddat > '2026-10-20 12:00+05' AND NOT bajarildi
ORDER BY muddat
LIMIT 5;

Natija:

text
 id |                      matn                      |   qolgan_vaqt   
----+------------------------------------------------+-----------------
 23 | Oshxonadagi jo'mrakni almashtirish #ta'mir #uy | 06:00:00
 31 | Python laboratoriya ishi #o'qish               | 1 day 20:30:00
 14 | IELTS writing task 2 mashqi #ingliz            | 4 days 08:00:00
 32 | Soliq hisobotini topshirish #ish #to'lov       | 5 days 06:00:00
 34 | SQL JOIN mashqlarini yechish #o'qish           | 5 days 09:00:00
(5 rows)

"Muddatga 6 soat qoldi" — ilova uchun tayyor ma'lumot.

5.3 now() tranzaksiya ichida o'zgarmaydi

now() — tranzaksiya boshlangan vaqt, soatning hozirgi ko'rsatkichi emas. Butun tranzaksiya davomida u bir xil. Haqiqiy soat — clock_timestamp(). Kanon (PostgreSQL 18.6):

sql
BEGIN;
SELECT now() = now() AS teng,
       pg_sleep(0.2),
       now() = transaction_timestamp() AS tx_vaqti,
       clock_timestamp() > now() AS soat_oldinda;
COMMIT;

Natija:

text
BEGIN
 teng | pg_sleep | tx_vaqti | soat_oldinda 
------+----------+----------+--------------
 t    |          | t        | t
(1 row)

COMMIT

pg_sleep(0.2) — 0.2 soniya kutish. Kutishdan keyin ham now() tranzaksiya vaqtiga teng, clock_timestamp() esa oldinga ketdi.

Nega bu yaxshi? Bitta tranzaksiyada 100 ta vazifa yaratilsa, hammasining yaratilgan_vaqt i bir xil bo'ladi — ular "bir amal" sifatida ko'rinadi. INSERT va RETURNING darsida now() - yaratilgan_vaqt nol chiqqani ham shu sababdan edi. Uzoq skriptda haqiqiy o'tgan vaqtni o'lchash kerak bo'lsa — clock_timestamp().

Tekshirib ko'ring: Ertalab soat 9:00 da boshlangan va 9:05 da tugagan tranzaksiyada yozilgan vazifaning DEFAULT now() vaqti qancha bo'ladi?

Javob

9:00 (tranzaksiya boshlangan payt), 9:05 emas. INSERT tranzaksiyaning qaysi daqiqasida bajarilganidan qat'i nazar.

6. Son funksiyalari

6.1 Yaxlitlash va boshqalar

Kanon (PostgreSQL 18.6):

sql
SELECT round(2.5) AS r1, round(3.5) AS r2, round(-2.5) AS r3,
       round(1234.5678, 2), trunc(1234.5678, 1),
       ceil(1.1), floor(-1.1),
       greatest(3, 9, 1), least(3, 9, NULL, 1) AS least_null,
       17 % 5 AS qoldiq, power(2, 10), sqrt(2);

Natija:

text
 r1 | r2 | r3 |  round  | trunc  | ceil | floor | greatest | least_null | qoldiq | power |        sqrt        
----+----+----+---------+--------+------+-------+----------+------------+--------+-------+--------------------
  3 |  4 | -3 | 1234.57 | 1234.5 |    2 |    -2 |        9 |          1 |      2 |  1024 | 1.4142135623730951
(1 row)
Funksiya Ish
round(x), round(x, n) Yaxlitlash (n xona)
trunc(x, n) Kesish, yaxlitlamasdan
ceil, floor Yuqoriga, pastga butun
greatest, least Ro'yxatdagi eng katta, eng kichik
% Bo'linma qoldig'i

Ikki nozik joy:

  • round(-2.5) = -3. numeric uchun "yarim" noldan uzoqqa yaxlitlanadi. JavaScript'da Math.round(-2.5) = -2 — farq qiladi! float8 (haqiqiy son) uchun esa boshqa qoida: biz ishga tushirdik (PostgreSQL 17.6) — round(2.5::numeric) → 3, round(2.5::float8) → 2, round(3.5::float8) → 4 ("juftga" yaxlitlash). Pul hisobida turni aniq biling (Pul, miqdor va enum'larni modellash).
  • least(3, 9, NULL, 1) = 1. greatest va least NULL ni e'tiborsiz qoldiradi — oddiy NULL qoidasidan (NULL mantiqi) istisno.

Nolga bo'lish — ERROR: division by zero; himoyasi — x / nullif(y, 0) (NULL mantiqi).

6.2 Interval bilan agregat

avg vaqt oraliqlari bilan ham ishlaydi. Kanon (PostgreSQL 18.6) — ustuvorlik bo'yicha o'rtacha bajarilish vaqti:

sql
SELECT ustuvorlik, count(*),
       avg(bajarilgan_vaqt - yaratilgan_vaqt) AS ortacha,
       justify_interval(avg(bajarilgan_vaqt - yaratilgan_vaqt))
         AS korinishi
FROM vazifalar
WHERE bajarildi
GROUP BY 1
ORDER BY 1;

Natija:

text
 ustuvorlik | count |     ortacha     |    korinishi    
------------+-------+-----------------+-----------------
 orta       |    21 | 23:36:08.571428 | 23:36:08.571428
 yuqori     |     1 | 00:05:00        | 00:05:00
(2 rows)

O'rta ustuvorlikdagilar o'rtacha ~23.6 soatda, yuqorisi (bitta — elektr to'lovi) 5 daqiqada bajarilgan. justify_interval — 30:00:00 kabi oraliqni 1 day 06:00:00 ga aylantiradi; bu yerda bir kundan kam, shuning uchun o'zgarmadi. "Past" yo'q — past ustuvorlikda bajarilgan vazifa yo'q. Bu kabi o'lchovlar keyinchalik Window funksiyalar bilan chuqurlashadi.

7. SQL qadami: ro'yxat uchun hisoblangan maydonlar (holat 21, commit yo'q)

Sxema o'zgarmaydi — holat 21 qoladi. Lekin ilova ro'yxati endi bazadan tayyor "holat" va "qolgan vaqt" ni olishi mumkin:

sql
SELECT id, matn, ustuvorlik, muddat,
  CASE WHEN bajarildi THEN 'bajarilgan'
       WHEN muddat < now() THEN 'muddati_otgan'
       WHEN muddat IS NULL THEN 'muddatsiz'
       ELSE 'faol' END AS holat,
  muddat - now() AS qolgan_vaqt
FROM vazifalar
WHERE egasi_id = 1 AND ota_id IS NULL
ORDER BY ustuvorlik DESC, muddat NULLS LAST, id;

Bu yerda haqiqiy now() — natija kunga qarab o'zgaradi, shuning uchun uni ko'rsatmaymiz. Qat'iy "bugun" bilan xuddi shu CASE ni «Qidiruvli CASE» bo'limida ko'rdik. ORDER BY ustuvorlik DESC, muddat NULLS LAST, id — ORDER BY darsidagi barqaror tartib: yuqori ustuvorlik, keyin eng yaqin muddat, muddatsizlar oxirida.

Qaror: "holat"ni bazada hisoblashmi yoki ilovada? Ikkalasi ham to'g'ri bo'lishi mumkin. Bazada — hisobot, filtr (WHERE holat = …) va boshqa mijozlar uchun bir xillik. Ilovada — foydalanuvchining soat mintaqasi va tili hisobga olinadi. Kanon bu "holat"ni bazada saqlamaydi. Holat 40 dagi faol_vazifalar ko'rinishi (view) — saqlangan SELECT — faqat o'chirilgan vazifalarni yashiradi, "holat" ustunini hisoblamaydi. Ko'rinish nima va qachon kerak — Soft delete, timestamps va tarix darsida.

8. Ko'p uchraydigan xatolar

Xato yoki belgi Sabab Davosi
7 / 2 → 3 Butun son bo'linmasi 7 / 2.0, ::numeric
invalid input syntax for type integer O'girib bo'lmaydigan matn Ma'lumotni tekshiring
Ism va familiyani ulash bo'sh natija berdi Biri NULL concat yoki concat_ws
substring bitta belgi siljigan Indeks 1 dan JS'dagi 0 ni unutib
CASE kutilmagan holat qaytardi Shartlar tartibi Eng muhimini birinchi
CASE NULL qaytardi ELSE yo'q ELSE qo'shing
Oy/kun chegarasi 5 soat siljigan date_trunc UTC'da Zona — Sana va vaqt zonalari
Bir tranzaksiyadagi vaqtlar bir xil now() — tranzaksiya vaqti clock_timestamp()
round(-2.5) JS bilan mos emas Yaxlitlash qoidasi farqi Turni va qoidani aniq biling

9. Mashqlar

1-mashq (oson): natijani toping

  • 9 / 4 →
  • concat('Bek', NULL, 'zod') →
  • split_part('osh,lagmon,manti', ',', 3) →
  • extract(month FROM '2026-10-24'::date) →
  • greatest(5, NULL, 7) →
Yechim
  • 2 — butun son bo'linmasi (2.25 emas).
  • Bekzod — concat NULL ni tashlaydi; || bilan NULL bo'lardi.
  • manti — uchinchi bo'lak, sanash 1 dan.
  • 10 — oktabr.
  • 7 — greatest NULL ni e'tiborsiz qoldiradi.

2-mashq (o'rta): ixcham ro'yxat

Holat 21 da Azizning asosiy vazifalari (ota_id IS NULL) uchun:

  • qisqa — matn 25 belgidan uzun bo'lsa, birinchi 25 belgi + …, aks holda to'liq matn;
  • belgi — ustuvorlik: yuqori !!!, o'rta !!, past !;
  • muddat — Toshkent vaqtida DD.MM HH24:MI shaklida, muddatsiz bo'lsa —.

Ishora: Toshkent vaqtiga o'tkazish — muddat AT TIME ZONE 'Asia/Tashkent' (buni SQL'da sana va vaqt zonalari darsida tushuntiramiz, hozir shablon sifatida ishlating). Formatlash — to_char(vaqt, 'DD.MM HH24:MI').

Yechim
sql
SELECT id,
       CASE WHEN char_length(matn) > 25
            THEN left(matn, 25) || '…'
            ELSE matn END AS qisqa,
       CASE ustuvorlik WHEN 'yuqori' THEN '!!!'
                       WHEN 'orta' THEN '!!'
                       ELSE '!' END AS belgi,
       coalesce(to_char(muddat AT TIME ZONE 'Asia/Tashkent',
                        'DD.MM HH24:MI'), '—') AS muddat
FROM vazifalar
WHERE egasi_id = 1 AND ota_id IS NULL
ORDER BY id;

Natija (biz ishga tushirdik, PG 17.6):

text
 id |           qisqa            | belgi |   muddat    
----+----------------------------+-------+-------------
  1 | JavaScript kursining 26-q… | !!!   | 31.10 23:59
  2 | Postgres'ni Docker'da ko'… | !!    | —
  8 | Bozordan kartoshka va piy… | !!    | —
  9 | Onamga dori olib borish #… | !!    | —
 17 | Portfolio saytiga blog yo… | !!    | 15.11 18:00
 23 | Oshxonadagi jo'mrakni alm… | !!!   | 20.10 18:00
 28 | Elektr to'lovini Click or… | !!!   | 10.10 23:59
 34 | SQL JOIN mashqlarini yech… | !!    | 25.10 21:00
 38 | Velosiped zanjirini moyla… | !     | —
(9 rows)

to_char(NULL, …) — NULL, coalesce uni — ga almashtirdi. Bazada muddat UTC'da (2026-10-31 18:59:00+00), Toshkent vaqtida esa 31.10 23:59 — oy oxiridagi yarim tun. || bu yerda xavfsiz: left(matn, 25) hech qachon NULL emas (matn — NOT NULL).

3-mashq (qiyin): teglar matndan

Vazifa matnidagi #teg larni funksiyalar bilan tahlil qiling (holat 21):

(a) Madinaning (egasi_id 4) har vazifasi uchun: teglar soni (matndagi # belgilari soni) va birinchi teg.

(b) Teglari eng ko'p 3 foydalanuvchi: egasi_id va jami teglar soni.

Ishora: # lar soni = matn uzunligi − # lar olib tashlangan matn uzunligi (replace). Birinchi teg — substring(matn FROM '#[^ ]+'): substring ga regex berilsa, birinchi mos bo'lakni qaytaradi ([^ ]+ — "bo'shliq bo'lmagan belgilar").

Yechim
sql
-- (a)
SELECT id,
       char_length(matn) - char_length(replace(matn, '#', ''))
         AS teglar_soni,
       substring(matn FROM '#[^ ]+') AS birinchi_teg
FROM vazifalar
WHERE egasi_id = 4
ORDER BY id;

-- (b)
SELECT egasi_id,
       sum(char_length(matn) - char_length(replace(matn, '#', '')))
         AS jami_teglar
FROM vazifalar
GROUP BY egasi_id
ORDER BY jami_teglar DESC, egasi_id
LIMIT 3;

Natija (biz ishga tushirdik, PG 17.6):

text
 id | teglar_soni | birinchi_teg 
----+-------------+--------------
  7 |           1 | #o'qish
 13 |           1 | #o'qish
 18 |           1 | #to'lov
 31 |           1 | #o'qish
 37 |           1 | #oila
 41 |           0 | 
 42 |           0 | 
 46 |           0 | 
 47 |           0 | 
(9 rows)

 egasi_id | jami_teglar 
----------+-------------
        1 |          10
        5 |           8
        2 |           7
(3 rows)

41, 42, 46, 47 — kichik vazifalar, ularda teg yo'q: substring NULL qaytardi (bo'sh katak). Bu usul sodda, lekin xatoga moyil: "Futbol: payshanba 20:00" dagi # bo'lmasa ham, "C# kursi" yoki "#1-o'rin" kabi matn noto'g'ri sanaladi. Shuning uchun UPSERT va MERGE darsida teglar alohida jadvalga, frontend TEG regex'ining aniq nusxasi bilan ajratiladi.

10. Real ishda

  • API'da hisoblangan maydonlar — holat, qolgan_vaqt, toliq_ism kabi maydonlar ko'pincha SELECT da CASE/funksiya bilan yasaladi; ORM'lar ham ularni "raw" ifoda sifatida qo'llab-quvvatlaydi.
  • Ma'lumot tozalash — import qilingan telefon, email, ismlar regexp_replace, lower, trim bilan bir xil shaklga keltiriladi — ko'pincha bitta UPDATE bilan.
  • Hisobotlar — date_trunc, extract, age, avg(interval) — dashboard'larning asosiy asbobi; to'g'ri zona bilan (Sana va vaqt zonalari).
  • Intervyu savollari: "now() va clock_timestamp() farqi?", "7 / 2 SQL'da nima beradi?", "COALESCE va CASE farqi?".

Xulosa

  • CASE WHEN … THEN … ELSE … END — qiymat qaytaradigan "agar"; birinchi mos shart yutadi, ELSE siz — NULL.
  • ::tur / CAST — tur o'girish; butun / butun = butun (7 / 2 = 3); o'girib bo'lmasa — xato.
  • Matn: || (NULL beradi) va concat (NULL ni tashlaydi), lower, trim, substring (1 dan), split_part, regexp_replace, format.
  • Vaqt: date_trunc, extract, age, interval; now() — tranzaksiya vaqti, clock_timestamp() — haqiqiy soat; zona masalasi — Sana va vaqt zonalari.
  • Son: round (numeric va float8 da qoida farq qiladi), greatest/least NULL ni e'tiborsiz qoldiradi, avg(interval).

Keyingi dars: UPSERT va MERGE — vazifa matnidagi #teg larni alohida jadvalga ajratamiz: INSERT … ON CONFLICT, DO NOTHING va DO UPDATE, MERGE … RETURNING.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
PostgreSQL funksiyalari: CASE, matn, sana va son funksiyalari — IlmHamroh