ENUM type — yaratish, ishlatish, o'zgartirish

~15 daq · PostgreSQL · PostgreSQL Maxsus Imkoniyatlari

Muammodan boshlaylik

Buyurtma holati (status) saqlash kerak: pending, processing, shipped, delivered, cancelled. Buni TEXT ustunida saqlasangiz — hech qanday kafolat yo'q. Kimdir "deliverd" (imlo xatosi), kimdir "DELIVERED" (katta harf), kimdir "done" yozishi mumkin. Ma'lumotlar xilma-xil bo'lib ketadi.

Yechim sifatida CHECK constraint qo'shish mumkin:

status TEXT CHECK (status IN ('pending', 'processing', 'shipped', 'delivered', 'cancelled'))

Ishlaydi — lekin har joyda bu ro'yxatni takrorlash kerak. Yangi holat qo'shilsa — barcha CHECK larni yangilash kerak.

ENUM — bu muammoni hal qiladi: qiymatlar bir joyda aniqlanadi, bazaning o'zi kafolat beradi.


ENUM nima?

ENUM — oldindan belgilangan qiymatlar to'plami. Faqat shu qiymatlar ruxsat etiladi, boshqa hech narsa.

Hayotiy o'xshatma: semaforda faqat uch rang — qizil, sariq, yashil. To'rtinchi rang yo'q. ENUM — shu "faqat ma'lum qiymatlar" qoidasi.

TEXT ustun:          ENUM:
──────────────────   ──────────────────────────────
"pending"  ✅        "pending"    ✅
"PENDING"  ✅        "PENDING"    ❌ xato!
"deliverd" ✅        "deliverd"   ❌ xato!
"done"     ✅        "done"       ❌ xato!
har narsa  ✅        faqat belgilangan qiymatlar ✅

ENUM yaratish

ENUM alohida tur sifatida yaratiladi, keyin ustunda ishlatiladi:

-- 1. Avval turni yaratish
CREATE TYPE buyurtma_holati AS ENUM (
  'pending',
  'processing',
  'shipped',
  'delivered',
  'cancelled'
);

-- 2. Jadvalda ishlatish
CREATE TABLE buyurtmalar (
  id      SERIAL PRIMARY KEY,
  mahsulot TEXT,
  holat   buyurtma_holati NOT NULL DEFAULT 'pending'
);

Ma'lumot qo'shish va o'qish

INSERT INTO buyurtmalar (mahsulot, holat) VALUES
  ('Noutbuk',    'pending'),
  ('Sichqon',    'processing'),
  ('Klaviatura', 'delivered');

-- ❌ — noto'g'ri qiymat
INSERT INTO buyurtmalar (mahsulot, holat) VALUES ('Monitor', 'done');
-- ERROR: invalid input value for enum buyurtma_holati: "done"

-- O'qish — oddiy matn kabi
SELECT * FROM buyurtmalar WHERE holat = 'pending';

ENUM ning yashirin xususiyati — tartiblash

ENUM qiymatlari yaratilgan tartibda saqlanadi — va shu tartibda taqqoslash mumkin:

-- Holat 'processing' dan keyin bo'lgan buyurtmalar
SELECT mahsulot, holat FROM buyurtmalar
WHERE holat > 'processing';
  mahsulot  │   holat
------------+-----------
 Klaviatura │ delivered

'shipped' va 'delivered' — 'processing' dan keyin. 'pending' esa oldinda — chiqmadi.

pending < processing < shipped < delivered < cancelled

Bu ENUM ning TEXT dan katta ustunligi — qiymatlar orasida mantiqiy tartib bor.


Mavjud ENUM ga yangi qiymat qo'shish

-- Oxiriga qo'shish
ALTER TYPE buyurtma_holati ADD VALUE 'returned';

-- Aniq joyga qo'shish
ALTER TYPE buyurtma_holati ADD VALUE 'on_hold' BEFORE 'processing';
ALTER TYPE buyurtma_holati ADD VALUE 'verified' AFTER 'pending';

⚠️ Muhim: Yangi qiymat qo'shish — tezkor va xavfsiz. Lekin mavjud qiymatni o'chirish yoki nomini o'zgartirish — PostgreSQL da to'g'ridan-to'g'ri imkonsiz. Bu ENUM ning asosiy cheklovi.


ENUM qiymatini o'zgartirish — murakkab yo'l

PostgreSQL da ALTER TYPE ... RENAME VALUE — PostgreSQL 10 dan:

-- Qiymat nomini o'zgartirish (PostgreSQL 10+)
ALTER TYPE buyurtma_holati RENAME VALUE 'cancelled' TO 'rejected';

Lekin qiymatni o'chirish uchun to'g'ridan-to'g'ri usul yo'q. Yechim:

-- 1. Yangi ENUM yaratish
CREATE TYPE buyurtma_holati_yangi AS ENUM ('pending', 'processing', 'shipped', 'delivered');

-- 2. Ustunni yangi turga o'tkazish
ALTER TABLE buyurtmalar
  ALTER COLUMN holat TYPE buyurtma_holati_yangi
  USING holat::TEXT::buyurtma_holati_yangi;

-- 3. Eskisini o'chirish
DROP TYPE buyurtma_holati;

-- 4. Nomini qaytarish
ALTER TYPE buyurtma_holati_yangi RENAME TO buyurtma_holati;

Bu muammolik — shu sababli ENUM ni dastlabki loyihalashda yaxshilab o'ylash kerak.


Mavjud ENUM qiymatlarini ko'rish

SELECT enum_range(NULL::buyurtma_holati);
-- {pending,processing,shipped,delivered,cancelled}

-- Barcha ENUM turlari
SELECT typname, enumlabel
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
WHERE typname = 'buyurtma_holati'
ORDER BY enumsortorder;
     typname      │  enumlabel
------------------+------------
 buyurtma_holati  │ pending
 buyurtma_holati  │ processing
 buyurtma_holati  │ shipped
 buyurtma_holati  │ delivered
 buyurtma_holati  │ cancelled

ENUM va CHECK constraint — qachon qaysi?

ENUM:                            CHECK constraint:
────────────────────────────     ────────────────────────────────
Bir joyda aniqlanadi             Har ustunda takrorlanadi
Tartiblash mumkin                Tartiblash yo'q
Qiymat o'chirish qiyin           Qiymat o'chirish — faqat ALTER
Ko'p jadvalda ishlatish qulay    Har jadvalda alohida yozish
Migratsiyada murakkab            Migratsiyada sodda

ENUM tanlang:

  • Qiymatlar barqaror, kam o'zgaradi
  • Ko'p jadvalda bir xil qiymatlar
  • Tartiblash (holat progressi) muhim

CHECK tanlang:

  • Qiymatlar tez-tez o'zgarishi mumkin
  • Faqat bitta jadvalda kerak
  • Migratsiyalarni sodda ushlatmoqchi

Tez-tez uchraydigan xatolar

Katta-kichik harf farqi:

-- ❌ — ENUM katta-kichik harfga sezgir
INSERT INTO buyurtmalar (holat) VALUES ('Pending');
-- ERROR: invalid input value for enum buyurtma_holati: "Pending"

-- ✅ — aynan yaratilgandagi shakl
INSERT INTO buyurtmalar (holat) VALUES ('pending');

ENUM qiymatlari aynan yozilgandek saqlanadi. 'pending' va 'Pending' — boshqa-boshqa.

Migratsiyada qiymat o'chirishga urinish:

-- ❌ — bu ishlamaydi
ALTER TYPE buyurtma_holati DROP VALUE 'cancelled';
-- ERROR: syntax error

-- ✅ — qayta yaratish yo'li (yuqorida ko'rsatildi)

ENUM yaratishdan oldin qiymatlar to'liq aniqlansin. O'chirish kerak bo'lsa — katta ish.


Xulosa

ENUM — oldindan belgilangan qiymatlar to'plami. Noto'g'ri qiymat kiritishdan himoya, tartiblash imkoniyati, bir joyda ta'rif.

CREATE TYPE tur_nomi AS ENUM ('qiymat1', 'qiymat2', ...);
ALTER TYPE tur_nomi ADD VALUE 'yangi';            -- qo'shish — oson
ALTER TYPE tur_nomi RENAME VALUE 'eski' TO 'yangi'; -- o'zgartirish — PostgreSQL 10+
-- O'chirish — to'g'ridan-to'g'ri imkonsiz, qayta yaratish kerak

Asosiy qoida: ENUM qiymatlari barqaror bo'lsa — ajoyib tanlov. Tez-tez o'zgarishi mumkin bo'lsa — CHECK yoki alohida lookup jadval yaxshiroq.


Keyingi dars

29-dars: Generated Columns va Computed Values