Generated Columns va Computed Values

~15 daq · PostgreSQL · PostgreSQL Maxsus Imkoniyatlari

Muammodan boshlaylik

Mahsulotlar jadvalida narx va miqdor ustunlari bor. Har bir buyurtmaning umumiy qiymati — narx × miqdor. Siz bu hisoblashni har safar qo'lda qilasiz:

SELECT nomi, narx * miqdor AS jami FROM mahsulotlar;

Yoki ilovaning o'zida hisoblaysiz, keyin bazaga yozasiz. Lekin bir muammo bor: narx o'zgarsa — saqlab qo'yilgan jami eskirib qoladi. Bazada narx = 500, ilovada hisoblangan jami = 5000 — lekin narx 600 ga o'zgargan edi. Ma'lumot mos emas.

Bunday holatda Generated Column kerak: baza o'zi hisoblaydi, o'zi yangilab turadi — siz hech narsa qilmaysiz.


Generated Column nima?

Generated Column — qiymati boshqa ustunlar asosida avtomatik hisoblanadigan ustun. Ma'lumotni qo'lda yozmaysiz — PostgreSQL har safar formulani ishlatadi.

Hayotiy o'xshatma: Excel dagi formula. A katakchaga narx, B katakchaga miqdor yozsangiz — C katakchada =A*B formulasi yozilgan. A yoki B o'zgarsa, C darhol yangilanadi. Siz C ni qo'lda kiritmaganingiz kabi, Generated Column ham qo'lda to'ldirilmaydi — baza o'zi qiladi.

narx  │ miqdor │ jami (generated)
──────┼────────┼──────────────────
 500  │   10   │  5000    ← baza hisobladi
 800  │    5   │  4000    ← baza hisobladi
 300  │   20   │  6000    ← baza hisobladi

narx o'zgardi (500 → 600):
 600  │   10   │  6000    ← baza o'zi yangiladi ✅

Yaratish sintaksisi

CREATE TABLE buyurtma_qatorlari (
  id       SERIAL PRIMARY KEY,
  nomi     TEXT    NOT NULL,
  narx     NUMERIC NOT NULL,
  miqdor   INT     NOT NULL,

  jami     NUMERIC GENERATED ALWAYS AS (narx * miqdor) STORED
  --                                    ↑ formula       ↑ diskda saqlash
);

GENERATED ALWAYS AS (formula) STORED — bu kalit so'zlar.

GENERATED ALWAYS — bu ustunni qo'lda o'zgartirib bo'lmaydi, faqat formula ishlaydi.

STORED — hisoblangan qiymat diskda saqlanadi. PostgreSQL da hozircha faqat STORED qo'llab-quvvatlanadi — VIRTUAL (hisoblangan lekin saqlanmagan) hali yo'q.


Amalda qanday ishlaydi

INSERT INTO buyurtma_qatorlari (nomi, narx, miqdor) VALUES
  ('Noutbuk',    5000000, 2),
  ('Sichqon',     150000, 5),
  ('Klaviatura',  350000, 3);

SELECT nomi, narx, miqdor, jami FROM buyurtma_qatorlari;
   nomi      │   narx    │ miqdor │    jami
-------------+-----------+--------+-----------
 Noutbuk     │ 5 000 000 │   2    │ 10 000 000
 Sichqon     │   150 000 │   5    │    750 000
 Klaviatura  │   350 000 │   3    │  1 050 000

jami ni siz yozmadingiz — PostgreSQL o'zi hisobladi.

Narx yangilansa:

UPDATE buyurtma_qatorlari SET narx = 200000 WHERE nomi = 'Sichqon';

SELECT nomi, narx, jami FROM buyurtma_qatorlari WHERE nomi = 'Sichqon';
  nomi   │  narx   │   jami
---------+---------+---------
 Sichqon │ 200 000 │ 1000000   ← avtomatik yangilandi ✅

Ko'proq misollar

Generated column faqat arifmetika emas — har qanday deterministik ifoda bo'lishi mumkin.

Matn birlashtirish

CREATE TABLE foydalanuvchilar (
  id          SERIAL PRIMARY KEY,
  ism         TEXT NOT NULL,
  familiya    TEXT NOT NULL,
  toʻliq_ism  TEXT GENERATED ALWAYS AS (ism || ' ' || familiya) STORED
);

INSERT INTO foydalanuvchilar (ism, familiya) VALUES ('Ali', 'Karimov');

SELECT toʻliq_ism FROM foydalanuvchilar;
-- Ali Karimov

Katta-kichik harf normallashtirish

Qidiruvda katta-kichik harf muammosini hal qilish uchun:

CREATE TABLE mahsulotlar (
  id          SERIAL PRIMARY KEY,
  nomi        TEXT NOT NULL,
  nomi_lower  TEXT GENERATED ALWAYS AS (LOWER(nomi)) STORED
);

CREATE INDEX ON mahsulotlar (nomi_lower);

-- Endi katta-kichik harfsiz tez qidirish mumkin
SELECT * FROM mahsulotlar
WHERE nomi_lower = LOWER('noutbuk PRO');

LOWER(nomi) ustuniga index qo'yish mumkin emas — lekin generated column ga qo'ysa bo'ladi.

Sana hisoblash

CREATE TABLE shartnomalar (
  id           SERIAL PRIMARY KEY,
  boshlanish   DATE NOT NULL,
  tugash       DATE NOT NULL,
  davomiylik   INT  GENERATED ALWAYS AS (tugash - boshlanish) STORED
  -- kun hisobida davomiylik
);

INSERT INTO shartnomalar (boshlanish, tugash)
VALUES ('2024-01-01', '2024-12-31');

SELECT davomiylik FROM shartnomalar;
-- 365

Generated column ga yozib bo'lmaydi

Generated column ni qo'lda o'zgartirishga urinish xato beradi:

-- ❌ — qo'lda yozish mumkin emas
INSERT INTO buyurtma_qatorlari (nomi, narx, miqdor, jami)
VALUES ('Monitor', 2800000, 1, 9999999);
-- ERROR: cannot insert a non-DEFAULT value into column "jami"

-- ❌ — yangilash ham mumkin emas
UPDATE buyurtma_qatorlari SET jami = 9999999 WHERE id = 1;
-- ERROR: column "jami" can only be updated to DEFAULT

Bu xavfsizlik kafolati — formula hamisha to'g'ri natija beradi, qo'lda buzib bo'lmaydi.


Generated column cheklovlari

Generated column formulasida ba'zi narsalar bo'lmaydi:

✅ Ishlaydi:
   Boshqa ustunlar         → narx * miqdor
   Matematik funksiyalar   → ROUND(narx * 0.12, 2)
   Matn funksiyalari       → LOWER(nomi), TRIM(matn)
   Sana funksiyalari       → tugash - boshlanish
   CASE ifodasi            → CASE WHEN narx > 1000000 THEN 'qimmat' ELSE 'arzon' END

❌ Ishlamaydi:
   Boshqa jadvalga murojaat  → (SELECT nomi FROM ...)
   Sequence                   → nextval(...)
   O'zgaruvchan funksiyalar   → now(), random()
   Boshqa generated column    → boshqa_generated * 2

Nima uchun now() ishlamaydi? now() har safar boshqa qiymat qaytaradi — generated column esa deterministik bo'lishi kerak, ya'ni kirish qiymatlari bir xil bo'lsa, natija har doim bir xil chiqishi shart.


DEFAULT va Generated Column — farqi

Ikkisi ham ustunni avtomatik to'ldiradi — lekin farqlari bor:

DEFAULT:                         Generated Column:
────────────────────────────     ────────────────────────────────
Faqat qo'shishda ishlaydi        Qo'shish va yangilashda ishlaydi
Qo'lda o'zgartirib bo'ladi       Qo'lda o'zgartirib bo'lmaydi
Boshqa ustunlarga bog'liq emas   Boshqa ustunlar asosida hisoblanadi
now(), random() ishlaydi         Deterministik funksiyalar kerak
-- DEFAULT — bir marta qo'shilganda qiymat beriladi, keyin mustaqil
sana TIMESTAMPTZ DEFAULT now()   -- qo'shilganda vaqt yozildi, o'zgarmaydi

-- Generated — har doim formulaga bog'liq
jami NUMERIC GENERATED ALWAYS AS (narx * miqdor) STORED  -- narx o'zgarsa, jami ham o'zgaradi

Tez-tez uchraydigan xatolar

Formulada boshqa jadvalga murojaat qilish:

-- ❌ — subquery ishlamaydi
jami NUMERIC GENERATED ALWAYS AS (
  (SELECT SUM(narx) FROM boshqa_jadval WHERE ...)
) STORED;
-- ERROR: cannot use subquery in column generation expression

Generated column faqat o'sha qatorning boshqa ustunlari bilan ishlaydi — boshqa jadvalga murojaat imkonsiz.

STORED kalit so'zini unutish:

-- ❌ — STORED yozilmadi
jami NUMERIC GENERATED ALWAYS AS (narx * miqdor);
-- ERROR: generated columns must be stored

-- ✅
jami NUMERIC GENERATED ALWAYS AS (narx * miqdor) STORED;

PostgreSQL da hozircha STORED majburiy — VIRTUAL qo'llab-quvvatlanmaydi.


Xulosa

Generated Column — qiymati boshqa ustunlar asosida avtomatik hisoblanadigan ustun. Excel dagi formula kabi — manba o'zgarsa, natija o'zgaradi.

GENERATED ALWAYS AS (formula) STORED

Qachon kerak:

  • Hisoblangan qiymatni tez-tez o'qish kerak, lekin har safar hisoblash qimmat
  • Index qo'yish kerak bo'lgan hisoblangan ustun (LOWER(nomi), narx * miqdor)
  • Ma'lumot izchilligini kafolatlash — qo'lda xato kiritishdan himoya

Cheklov: Deterministik bo'lishi shart, subquery yo'q, now() va random() yo'q, boshqa generated column ga bog'liq bo'lmaydi.


Keyingi dars

30-dars: Built-in funksiyalar — string, number, date, conditional