ENUM type — yaratish, ishlatish, o'zgartirish
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