10 β To'g'ri ma'lumot turini tanlash (PostgreSQL boy turlari)¶
β¬ οΈ Oldingi: 09 β Logik modeldan fizik sxemaga Β· π README Β· Keyingi: 11 β Yaxlitlik va constraint dizayni β‘οΈ
Bu bobda: ustunga tur tanlash β bu shunchaki "raqammi yo matnmi" emas, balki DIZAYN qarori. Noto'g'ri tur bug, sekinlik va joy isrofiga olib keladi. Pulni nega float'da saqlash mumkin emasligini (haqiqiy 0.1+0.2 xatosi bilan),
timestamptznegatimestampdan ustunligini, JSONB qachon dono va qachon tuzoq ekanini, array/enum/range/uuid/domain/generated column'larni qachon ishlatishni β hammasini PostgreSQL 18 da haqiqatan ishga tushirib ko'rsatamiz.
0. Tur tanlash β bu dizayn qaror, sintaksis emas¶
09-bobda logik modelni fizik PostgreSQL sxemasiga o'tkazdik va tur tanlashga kirish qildik. Endi har bir ustun turini ATAYLAB tanlashni o'rganamiz.
CREATE TABLE yozish oson. Lekin har column_name <type> qatorida siz aslida to'rtta narsani bir vaqtda hal qilasiz:
- Ma'no β bu ustun nimani anglatadi? (
narxpul,created_atlahza,is_activemantiq) - Yaxlitlik β qanday qiymatlar JOIZ? (
age >= 0, email shaklida, faqat 5 ta holatdan biri) - Aniqlik β qiymat ANIQ saqlanadimi yoki taxminanmi? (pulda 1 tiyin ham muhim)
- Performans β bu ustun bo'yicha qanday indeks/so'rov ishlaydi? (numeric tez taqqoslanadi, JSONB GIN talab qiladi)
Yomon tur tanlash narxi:
- Bug: pulni
floatda saqlasangiz, hisob-kitob 1 tiyinga adashadi (pastda haqiqiy misol). - Sekinlik: sanani
textda ('2026-06-13') saqlasangiz, oraliq so'rovi va indeks ishlamaydi. - Joy isrofi: har qatorda
biginto'rniga... aksincha,intyetarli bo'lsa-yubigintqo'ysangiz β har qatorda 4 bayt ortiqcha. - Yaxlitlik teshigi: holatni erkin
textda saqlasangiz,'tolangan','Tolangan','paid'β uchchovi bir jadvalda yashaydi.
Oltin qoida: ma'noga eng yaqin, eng tor turni tanla. "Shubhada bo'lsa
text/numericqo'yaman" emas β balki "bu maydon haqiqatan nimani ifodalaydi?" deb so'ra. To'g'ri tur β bu bepul yaxlitlik va bepul hujjat.
Bu bob PostgreSQL 18 ga qaratilgan. Boshqa bazalardagi muhim farqlarni kerakli joyda "MySQL'da:" izohi bilan beramiz.
1. Son turlari: numeric vs float β eng qimmat xato¶
1.1 Pulni nega float'da SAQLAB BO'LMAYDI¶
float (PostgreSQL'da real = 4 bayt, double precision/float8 = 8 bayt) β bu ikkilik suzuvchi nuqta turi. U IEEE 754 standartiga ko'ra sonni ikkilik kasr sifatida saqlaydi. Muammo shundaki, 0.1 kabi oddiy o'nlik kasr ikkilikda ANIQ ifodalanmaydi β xuddi 1/3 ni o'nlikda 0.333... deb cheksiz yozganimizdek.
Bu nazariy emas. PostgreSQL 18 da haqiqatan ishga tushirdik:
SELECT 0.1::float8 + 0.2::float8 AS float_yigindi,
(0.1::float8 + 0.2::float8) = 0.3 AS float_teng_mi;
0.1 + 0.2 0.3 ga teng emas β natija 0.30000000000000004. Endi tasavvur qiling: do'kon savatida 10 ta mahsulot, har biri 0.10 so'm. Float bilan jamlasak:
Kutilgan 1.00 o'rniga 0.9999999999999999. Yetarlicha tranzaksiyadan keyin balansingiz tiyinlarga adashadi va buxgalter sizni topadi. Bu klassik "float'da pul" anti-naqshi (13-bobda yana ko'ramiz).
1.2 numeric β aniq o'nlik¶
numeric(p, s) (sinonimi decimal) β bu aniq o'nlik turi. p (precision) β jami muhim raqamlar soni, s (scale) β kasr xonalari soni. Pul uchun odatda numeric(p, 2).
SELECT 0.1::numeric + 0.2::numeric AS numeric_yigindi,
(0.1::numeric + 0.2::numeric) = 0.3 AS numeric_teng_mi;
Aynan 0.3, va = 0.3 rost. O'sha 10 ta 0.10 ni jamlasak:
SELECT sum(x)::numeric AS numeric_sum
FROM (SELECT 0.10::numeric(12,2) AS x FROM generate_series(1,10)) t;
Aniq 1.00. numeric ustun saqlashda berilgan masshtabga yaxlitlanadi:
Qaror qoidasi: narx, balans, soliq, foiz, har qanday pul yoki "aniq bo'lishi shart" son β
numeric/decimal.float/realni faqat taxminiy o'lchovlar uchun ishlat: sensor o'qishi, geo-koordinata, ilmiy hisob, ML xususiyatlari. Ularda bir milliarddan bir xato muhim emas, lekin tezlik va joy muhim.MySQL'da: xuddi shu β
DECIMAL(p,s)pul uchun,FLOAT/DOUBLEtaxminiy uchun. Tamoyil bir xil.
1.3 Butun son turlari: smallint / integer / bigint¶
Butun sonlar uchun uchta o'lcham bor. Diapazonni va baytni PG18 tasdiqladi:
SELECT pg_column_size(1::smallint) AS smallint_bayt,
pg_column_size(1::int) AS int_bayt,
pg_column_size(1::bigint) AS bigint_bayt;
| Tur | Bayt | Diapazon | Qachon |
|---|---|---|---|
smallint |
2 | -32 768 ... 32 767 | yosh, yil, mayda hisoblagich, enum-kod |
integer (int) |
4 | ~ -2.1 mlrd ... 2.1 mlrd | odatiy tanlov: ko'pchilik ID, miqdor |
bigint |
8 | ~ -9.2e18 ... 9.2e18 | katta ID-ketma-ketlik, baytlar, vaqt belgisi |
Dizayn maslahati: auto-o'suvchi PRIMARY KEY uchun ko'p loyihalarda bigint (bigint GENERATED ALWAYS AS IDENTITY) standart bo'lib qoldi β chunki tabiat qonuni: muvaffaqiyatli loyiha int chegarasiga (2.1 mlrd) yetib qoladi, keyin uni bigintga o'tkazish katta jadvalda og'riqli migratsiya (23-bobga qarang). 8 bayt arzon, kechiktirilgan migratsiya qimmat. Kalit dizayni haqida 06-bobga qarang.
MySQL'da:
TINYINT/SMALLINT/MEDIUMINT/INT/BIGINTbor vaUNSIGNEDvarianti ham bor. PostgreSQL'daUNSIGNEDyo'q β o'rnigaCHECK (x >= 0)yoki domen ishlatiladi (1.x bo'limida domen).
2. Matn turlari: text vs varchar(n) vs char(n)¶
2.1 PostgreSQL'da text va varchar(n) deyarli bir xil¶
Ko'p dasturchi MySQL odatidan kelib, "qancha belgi kerak?" deb o'ylab varchar(255) yozadi. PostgreSQL'da bu deyarli ma'nosiz: text va varchar(n) bir xil ichki saqlash mexanizmiga ega (variable-length, TOAST). varchar(n) shunchaki text + length(value) <= n CHECK qoidasi.
CREATE TABLE t_test (a text, b varchar(100), c char(10));
INSERT INTO t_test VALUES ('salom', 'salom', 'salom');
SELECT pg_column_size(a) AS text_bayt, pg_column_size(b) AS varchar_bayt
FROM t_test;
Bir xil 6 bayt. Demak varchar(n) ni faqat haqiqatan biznes chegarasi bo'lsa ishlating ("telefon raqami 20 belgidan oshmaydi") β chegarani ifodalash uchun, performans uchun emas. Aks holda oddiy text qo'ying; chegara keyin kerak bo'lsa, CHECK qo'shasiz (va varchar(n) ni kattalashtirish PG18 da metadata-only, lekin kichraytirish jadvalni qayta tekshiradi).
2.2 char(n) β tuzoq, deyarli hech qachon kerak emas¶
char(n) (character(n)) β bu qat'iy uzunlikdagi, probel bilan to'ldirilgan tur. Aynan shu to'ldirish tuzoq tug'diradi:
INSERT INTO t_test VALUES ('salom', 'salom', 'salom');
SELECT (c = 'salom') AS char_teng, (a = 'salom ') AS text_teng FROM t_test;
char(10) ustunidagi 'salom' 'salom' ga teng (oxiridagi probellar taqqoslashda e'tiborga olinmaydi), lekin text ustunidagi 'salom' 'salom ' ga teng emas (probellar muhim). Bu nomuvofiqlik foydalanuvchini chalg'itadi, dasturda kutilmagan bug tug'diradi va char(n) saqlashda joy ham tejamaydi. Maslahat: char(n) ni ishlatmang. ISO mamlakat kodi ('UZ') kabi haqiqatan qat'iy uzunlikli narsada ham text + CHECK (length(x) = 2) ishonchli va kutilgan tarzda ishlaydi.
MySQL'da:
VARCHAR(n)danhaqiqatan o'lchamga ta'sir qiladi (indeks prefiks chegarasi, in-row saqlash) β shuning uchun MySQL'dan kelganlarvarchar(n)ga ko'proq e'tibor berishadi. PostgreSQL'da bu odatni unutish mumkin.
3. Vaqt turlari: timestamptz vs timestamp β har doim timestamptz¶
Bu β eng ko'p uchraydigan, lekin eng kech sezilgan xato. Ikki tur o'xshash ko'rinadi:
timestamp(timestamp without time zone) β "soat 12:00", lekin qaysi shaharning 12:00 ekani yozilmaydi. Bu "naive" lahza.timestamptz(timestamp with time zone) β aniq lahzani ichida UTC sifatida saqlaydi va o'qiyotgan sessiyaning vaqt mintaqasiga avtomatik o'giradi.
Farqni PG18 da ko'rsatamiz. Bir xil qiymatni Toshkent zonasida saqlab, keyin boshqa zonada o'qiymiz:
SET timezone = 'Asia/Tashkent';
CREATE TABLE hodisa (id int, tz timestamptz, notz timestamp);
INSERT INTO hodisa VALUES (1, '2026-06-13 12:00:00+00', '2026-06-13 12:00:00');
SELECT tz AS tz_tashkentda, notz AS notz_ozgarmas FROM hodisa;
tz_tashkentda | notz_ozgarmas
------------------------+---------------------
2026-06-13 17:00:00+05 | 2026-06-13 12:00:00
Endi New York zonasiga o'tib, AYNI o'sha qatorni o'qiymiz:
tz_newyorkda | notz_ozgarmas
------------------------+---------------------
2026-06-13 08:00:00-04 | 2026-06-13 12:00:00
E'tibor bering:
timestamptzbir xil lahzani ko'rsatadi: Toshkentda17:00 (+05), New Yorkda08:00 (-04)β ikkalasi ham aynan UTC12:00. Tur "qaysi lahza" ekanini biladi.timestamphar doim12:00β lekin bu qaysi shaharning 12:00 si? Hech kim bilmaydi. Yer yuzida foydalanuvchilar bo'lsa, bu ma'lumot buzuq.
Qaror qoidasi: lahzani (hodisa qachon yuz berdi:
created_at,paid_at,logged_in_at) saqlasangiz β HAR DOIMtimestamptz. Bu sukut bo'yicha tanlovingiz bo'lsin.timestamp(zonasiz) ni faqat zona ataylab ma'nosiz bo'lganda ishlating: masalan "har kuni mahalliy soat 09:00 da budilnik" yoki sof matematik vaqt.
Boshqa vaqt turlari:
dateβ faqat sana, soatsiz: tug'ilgan kun, hisobot kuni, ta'til sanasi.timeβ kun ichidagi vaqt, sanasiz: do'kon ochilish vaqti.intervalβ davomiylik:'2 days 3 hours','90 minutes'. Ikkitimestamptzayirmasiintervalberadi.
MySQL'da:
DATETIME(zonasiz,timestampga o'xshash) vaTIMESTAMP(UTC saqlaydi, lekin diapazoni 2038 gacha cheklangan va sessiya zonasiga bog'liq). MySQL'da xulq boshqacha va chalkash β PostgreSQL'ningtimestamptzmodeli ancha tiniq. Qaysi bazada bo'lsangiz ham, "UTC saqla, ko'rsatishda o'gir" tamoyiliga amal qiling.
4. boolean β uch qiymatli mantiq¶
boolean TRUE, FALSE va... NULL qiymatlarini oladi. Bu uch qiymat haqida 05-bobda gapirgandik β NULL "noma'lum" degani. is_active boolean NOT NULL DEFAULT true deb yozsangiz, ikki qiymatga majburlaysiz; NULLga ruxsat bersangiz, "ha/yo'q/noma'lum" uchligini modellaysiz (masalan so'rovnomada javob berilmagan).
Maslahat: boolean ustun nomini ijobiy va "ha/yo'q"li qiling:
is_active,tolangan,tasdiqlangan.is_not_disabledkabi qo'sh-inkor nomdan qoching β kod o'qishni qiyinlashtiradi.MySQL'da: alohida
BOOLEANturi YO'Q β uTINYINT(1)ning sinonimi.0/1saqlanadi va siz osonginatolangan = 5kabi noto'g'ri qiymat kiritib qo'yishingiz mumkin. PostgreSQLbooleani haqiqiy va qat'iy.
5. enum vs lookup-jadval β cheklangan ro'yxat trade-off'i¶
Ko'p ustunlar faqat oldindan ma'lum qiymatlar to'plamidan birini oladi: buyurtma holati, foydalanuvchi roli, to'lov usuli. Buni modellashning ikki yo'li bor.
5.1 PostgreSQL ENUM turi¶
CREATE TYPE buyurtma_holati AS ENUM ('yangi','tolangan','jonatildi','yetkazildi','bekor');
CREATE TABLE buyurtma (id int PRIMARY KEY,
holati buyurtma_holati NOT NULL DEFAULT 'yangi');
INSERT INTO buyurtma (id, holati) VALUES (1,'tolangan'),(2,'yangi');
SELECT id, holati FROM buyurtma ORDER BY holati;
Diqqat: ORDER BY holati alfavit emas, e'lon tartibi bo'yicha saralaydi (yangi < tolangan). Bu enum'ning yaxshi xususiyati β "holat oqimi" tabiiy tartiblanadi.
ENUM afzalliklari: ixcham (ichida kichik son sifatida saqlanadi), o'qish oson, tartibli, ustunda darhol yaxlitlik (noto'g'ri qiymat rad etiladi). Kamchiliklari: yangi qiymat qo'shish ALTER TYPE ... ADD VALUE talab qiladi (bu yengil, lekin migratsiya); qiymatni o'chirish/qayta nomlash qiyin; har bir enum qiymatiga qo'shimcha metadata (tavsif, tartib raqami, rang) biriktirib bo'lmaydi.
5.2 Lookup (ma'lumotnoma) jadval¶
CREATE TABLE buyurtma_holati_turi (
kod text PRIMARY KEY, -- 'yangi', 'tolangan', ...
nomi text NOT NULL, -- ko'rsatish uchun "Yangi buyurtma"
tartib smallint NOT NULL -- oqimdagi o'rni
);
CREATE TABLE buyurtma2 (
id int PRIMARY KEY,
holati text NOT NULL REFERENCES buyurtma_holati_turi(kod)
);
Lookup jadval afzalliklari: yangi holat β oddiy INSERT (DDL/migratsiya kerak emas, hatto runtime'da admin qo'shadi); har holatga metadata biriktirsa bo'ladi (tavsif, tartib, rang, faollik); boshqa jadvallar ham unga FK qo'yishi mumkin. Kamchiliklari: har so'rovda JOIN; nomli kod (text) o'rniga raqamli ID ishlatsangiz so'rov o'qishi murakkablashadi.
| Mezon | ENUM | Lookup jadval |
|---|---|---|
| Qiymatlar barqaror, kamdan-kam o'zgaradi | β enum yaxshi | ortiqcha |
| Qiymatlar tez-tez qo'shiladi/o'zgaradi | ALTER kerak | β INSERT |
| Har qiymatga metadata kerak (tavsif, rang) | β yo'q | β ustun qo'sh |
| Runtime'da admin boshqaradi | β | β |
| Eng ixcham saqlash / JOIN'siz | β | JOIN kerak |
Qaror qoidasi: kichik, deyarli o'zgarmas, "kod ichidagi" to'plam (masalan
booleandan biroz kengroq holat) β enum. Foydalanuvchi/admin boshqaradigan, metadata kerak, kengayib boruvchi to'plam β lookup jadval. Ikkilanaganda lookup jadval xavfsizroq tanlov, chunki kelajakda kengaytirish oson. (12-bobda holat modellashtirishni chuqurroq ko'ramiz.)MySQL'da:
ENUM('a','b','c')ustun turi sifatida bor (PostgreSQL'da alohida TYPE), lekin u o'zgartirish qiyin va ko'chirishda muammoli β MySQL hamjamiyatida ko'pchilik lookup jadvalni afzal ko'radi.
6. JSONB β qachon dono, qachon tuzoq¶
jsonb β PostgreSQL'ning eng kuchli va eng ko'p suiiste'mol qilinadigan turi. U JSON hujjatni ikkilik, indekslanadigan shaklda saqlaydi. (json turi ham bor β u xom matn saqlaydi, deyarli har doim jsonb afzal: tezroq so'raladi, indekslanadi, ortiqcha probel/dublikat kalitni tozalaydi.)
6.1 JSONB qachon o'rinli β dinamik atributlar¶
To'g'ri ishlatilganda JSONB ajoyib: har qatorda har xil bo'ladigan, oldindan to'liq noma'lum atributlar uchun. Klassik misol β turli mahsulot toifalari (telefonning xotirasi bor, kitobning muallifi bor, stulning materiali bor):
CREATE TABLE mahsulot (
id int PRIMARY KEY,
nom text NOT NULL,
narx numeric(12,2) NOT NULL, -- DOIMIY maydon -> oddiy ustun
atribut jsonb NOT NULL DEFAULT '{}' -- DINAMIK qism -> JSONB
);
INSERT INTO mahsulot VALUES
(1, 'Telefon', 4500000, '{"rang":"qora","xotira_gb":256,"5g":true}'),
(2, 'Kitob', 85000, '{"muallif":"Oqil","sahifa":320}'),
(3, 'Stul', 350000, '{"material":"yog och","rang":"jigarrang"}');
Diqqat: narx β har mahsulotda bor, doim so'raladi, shuning uchun u oddiy numeric ustun. Faqat o'zgaruvchan qism JSONB'ga ketdi.
JSONB so'rovlari:
-- @> containment: 256 GB xotirali mahsulotlar
SELECT id, nom FROM mahsulot WHERE atribut @> '{"xotira_gb":256}';
-- ->> operatori: qiymatni matn sifatida; ? operatori: kalit bor-yo'qligi
SELECT nom, atribut->>'rang' AS rang FROM mahsulot WHERE atribut ? 'rang';
6.2 JSONB'ni indekslash β GIN¶
JSONB ustunni filtrlash katta jadvalda sekin (har qatorni ochish kerak). Yechim β GIN indeks:
CREATE INDEX idx_mahsulot_atribut ON mahsulot USING gin (atribut);
EXPLAIN (COSTS off) SELECT id FROM mahsulot WHERE atribut @> '{"5g":true}';
QUERY PLAN
--------------------------------------------------------
Bitmap Heap Scan on mahsulot
Recheck Cond: (atribut @> '{"5g": true}'::jsonb)
-> Bitmap Index Scan on idx_mahsulot_atribut
Index Cond: (atribut @> '{"5g": true}'::jsonb)
So'rov rejasi Bitmap Index Scan on idx_mahsulot_atribut ni ko'rsatadi β GIN indeks @> (containment) so'rovini tezlashtirdi. GIN @>, ?, ?|, ?& operatorlarini qo'llab-quvvatlaydi. (Indeks turlari haqida 14-bobga qarang.)
Eslatma: bu reja jadval yetarlicha katta bo'lganda chiqadi. Bizning misoldagidek bir necha qatorli jadvalda planner indeksni e'tiborsiz qoldirib, baribir
Seq Scantanlashi mumkin β kichik jadvalni to'liq o'qish indeksdan ham arzon. GIN'ning haqiqiy foydasi minglab qatorda ko'rinadi (buni 14-bobda 100 mingdan ortiq qatorli jadvalda aniq o'lchaymiz).
Agar bitta aniq kalit doim so'ralsa va siz uni indekslamoqchi/cheklab qo'ymoqchi bo'lsangiz β uni JSONB'dan generated column sifatida tashqariga chiqaring (7-bo'limga qarang).
6.3 JSONB xavfi β nega sukut bo'yicha tanlov EMAS¶
JSONB qulaylik bilan birga jiddiy narx oladi:
- Tur tekshiruvi yo'q.
{"narx": "behisob"}ham,{"narx": -5}ham bemalol kiradi. Baza sizni himoya qilmaydi. - Yaxlitlik yo'q. JSONB ichidagi qiymatga
FOREIGN KEYyokiUNIQUEqo'yish qiyin va g'ayritabiiy. - Normalizatsiyaga zid. Bir xil qiymat (
"rang":"qora") minglab qatorda takrorlanadi β bu denormalizatsiya. Uni yangilash, agregatlash qiyin. - So'rov noqulay.
atribut->>'narx'har doim matn qaytaradi; raqam sifatida ishlatish uchun har joydacastkerak.
Qaror qoidasi (qaror daraxtidan): 1. Maydon oldindan ma'lum va barqaror (har qatorda bor, tez-tez filtrlanadi)? β oddiy ustun (
numeric,enum,timestamptz). Tur, FK, CHECK, indeks bepul. 2. Atributlar haqiqatan har xil va dinamik (mahsulot toifasiga qarab boshqa)? β JSONB + GIN indeks, muhim kalitlarni generated column qil. 3. Atributlar cheklangan, "kataloglanadigan" to'plam? β normallashtirilgan jadval (lookup/atribut jadvali) β EAV anti-naqshidan saqlanib (13-bob).Eng katta anti-naqsh: butun qatorni bitta JSONB
dataustuniga "tashlash" β bu sxemasiz, tekshiruvsiz bazaga aylantirish. JSONB β qo'shimcha, asos emas.MySQL'da:
JSONturi bor (MySQL 5.7+), lekin u PostgreSQL'ningjsonbsiga tengdek emas β generated column + indeks orqali indekslanadi, GIN'ga o'xshash to'g'ridan-to'g'ri operator indeksi yo'q. Tamoyil bir xil: dinamik qism uchun, asos uchun emas.
7. generated column β STORED va PG18 VIRTUAL¶
Generated column β qiymati boshqa ustunlardan IFODA orqali avtomatik hisoblanadigan ustun. Siz unga yoza olmaysiz; baza o'zi hisoblaydi. Bu denormalizatsiyaning eng xavfsiz shakli, chunki qiymat hech qachon "drift" qilmaydi (manba bilan nomuvofiq bo'lib qolmaydi).
7.1 STORED β diskda saqlanadi¶
CREATE TABLE buyurtma_qatori (
id int PRIMARY KEY,
miqdor int NOT NULL,
birlik_narx numeric(12,2) NOT NULL,
jami numeric(12,2) GENERATED ALWAYS AS (miqdor * birlik_narx) STORED
);
INSERT INTO buyurtma_qatori (id, miqdor, birlik_narx) VALUES (1, 3, 2500.00), (2, 10, 99.90);
SELECT id, miqdor, birlik_narx, jami FROM buyurtma_qatori;
id | miqdor | birlik_narx | jami
----+--------+-------------+---------
1 | 3 | 2500.00 | 7500.00
2 | 10 | 99.90 | 999.00
jami o'zi hisoblandi (3 * 2500 = 7500). STORED qiymat diskda saqlanadi β o'qish tez, indekslab bo'ladi, lekin joy egallaydi va yozishda hisoblanadi.
7.2 VIRTUAL β hisoblab beriladi (PostgreSQL 18+)¶
PostgreSQL 18 dan boshlab VIRTUAL generated column ham bor: qiymat diskda saqlanmaydi, o'qishda hisoblab beriladi. Joy tejaydi, yozish arzon β lekin har o'qishda hisoblanadi va (hozircha) to'g'ridan-to'g'ri indekslab bo'lmaydi. PG18 (port 5434) da haqiqatan tasdiqladik:
-- VIRTUAL generated column (PostgreSQL 18+)
CREATE TABLE inson (
id int PRIMARY KEY,
ism text NOT NULL,
familiya text NOT NULL,
toliq_ism text GENERATED ALWAYS AS (ism || ' ' || familiya) VIRTUAL
);
INSERT INTO inson (id, ism, familiya) VALUES (1, 'Oqil', 'Imomnazarov');
SELECT id, toliq_ism FROM inson;
VIRTUAL ishladi β toliq_ism ustuni saqlanmagani holda hisoblab berildi.
STORED vs VIRTUAL qaror qoidasi: ifoda qimmat va tez-tez o'qiladi yoki indekslanishi kerak bo'lsa β STORED (hisobni bir marta yozishda qil). Ifoda arzon, joy muhim, kamdan-kam o'qiladi β VIRTUAL (PG18+). To'liq ism, formatlash kabi yengil narsalar uchun VIRTUAL mukammal.
7.3 JSONB kalitini ustunga chiqarish¶
Generated column JSONB'dagi muhim kalitni "haqiqiy ustun" qilib, indekslash/cheklash imkonini beradi:
CREATE TABLE hujjat (
id int PRIMARY KEY,
data jsonb NOT NULL,
muallif text GENERATED ALWAYS AS (data->>'muallif') STORED
);
INSERT INTO hujjat (id, data) VALUES (1, '{"muallif":"Oqil","yil":2026}');
SELECT id, muallif FROM hujjat;
Endi muallif ustuniga oddiy B-tree indeks qo'yib, JSONB'dagi qiymatni tez filtrlash mumkin β JSONB qulayligi + ustun tezligi.
MySQL'da: generated column bor (
GENERATED ALWAYS AS (...) STORED|VIRTUAL) β MySQL'da VIRTUAL allaqachon bor edi. PostgreSQL'ga VIRTUAL faqat 18-versiyada qo'shildi.
8. array, range, uuid β yana uchta boy tur¶
8.1 array β qachon o'rinli, qachon normalizatsiyaga zid¶
PostgreSQL ustunda massiv saqlay oladi (text[], int[]). Bu kichik, tartibi muhim bo'lishi mumkin, alohida so'rov/FK talab qilmaydigan ro'yxat uchun mos β masalan maqola teglari:
CREATE TABLE maqola (
id int PRIMARY KEY,
sarlavha text NOT NULL,
teglar text[] NOT NULL DEFAULT '{}'
);
INSERT INTO maqola VALUES
(1,'PostgreSQL 18 yangiliklari', ARRAY['postgresql','baza','sql']),
(2,'Pul turlari', ARRAY['baza','dizayn']);
-- 'baza' tegi bor maqolalar (@> containment)
SELECT id, sarlavha FROM maqola WHERE teglar @> ARRAY['baza'];
Array ustunni ham GIN bilan indekslash mumkin: CREATE INDEX ... USING gin (teglar);
Lekin ehtiyot bo'ling β array ko'pincha normalizatsiyaga zid. Agar:
- teg/element alohida entity bo'lsa (o'z nomi, tavsifi, soni bor),
- unga FK kerak bo'lsa (teg jadvaliga ishora),
- "qaysi teg eng ko'p ishlatilgan?" kabi agregat kerak bo'lsa β
unda bu N:M bog'lanish (04-bobga qarang): maqola, teg, maqola_teg (junction) jadvallari. Array β bu denormalizatsiya; faqat ro'yxat haqiqatan "bitta qiymat" kabi ishlaganda o'rinli (masalan rangli rasm RGB qiymatlari, ruxsat bayroqlari).
MySQL'da: mahalliy array turi YO'Q. Odatda JSON massivi yoki junction jadval ishlatiladi. PostgreSQL array β uning o'ziga xos kuchi.
8.2 range β oraliqlar va EXCLUDE bilan ustma-ustlikni taqiqlash¶
range turlari (int4range, num2range, tsrange, tstzrange, daterange) oraliqni bitta qiymat sifatida saqlaydi: narx oralig'i, amal qilish davri, band qilingan vaqt. Eng kuchli qo'llanishi β EXCLUDE constraint bilan ustma-ust tushishni taqiqlash (masalan bitta xona ikki marta bron qilinmasin):
CREATE TABLE bron (
xona int NOT NULL,
davr tsrange NOT NULL,
EXCLUDE USING gist (xona WITH =, davr WITH &&) -- bir xona + ustma-ust davr taqiqlanadi
);
INSERT INTO bron VALUES (101, '[2026-06-13 14:00, 2026-06-13 16:00)');
-- Endi ustma-ust tushadigan bron:
INSERT INTO bron VALUES (101, '[2026-06-13 15:00, 2026-06-13 17:00)');
Ikkinchi INSERT PG18 da rad etildi:
ERROR: conflicting key value violates exclusion constraint "bron_xona_davr_excl"
DETAIL: Key (xona, davr)=(101, ["2026-06-13 15:00:00","2026-06-13 17:00:00")) conflicts
with existing key (xona, davr)=(101, ["2026-06-13 14:00:00","2026-06-13 16:00:00")).
Baza darajasida, dastur kodisiz, ikki bron 15:00β16:00 da ustma-ust kelishi taqiqlandi. (EXCLUDE constraint'ni 11-bobda chuqurroq ko'ramiz.)
8.3 uuid β taqsimlangan kalit¶
uuid β 128-bitli universal noyob identifikator. Taqsimlangan tizimda (bir nechta server bir jadvalga yozadi, yoki ID'ni mijoz/oflayn yaratadi) auto-increment ID'ga qarshi tabiiy yechim. PostgreSQL 18 da vaqt-tartibli uuidv7() funksiyasi bor (oldingi uuidv4() tasodifiy edi β indeksda yomon joylashardi):
uuidv7() ichida vaqt belgisi borligi uchun yangi qatorlar indeksning oxiriga tartibli tushadi β uuidv4() ning tasodifiy tarqalishidan kelib chiqadigan indeks shishishi muammosini hal qiladi. Kalit turlari (UUID v4 vs v7) haqida 06-bobga qarang.
MySQL'da: mahalliy
UUIDustun turi yo'q β odatdaBINARY(16)da saqlanadi,UUID()funksiyasi v1 beradi. PostgreSQL'dauuidhaqiqiy tur.
9. domain β qayta ishlatiluvchi CHECK¶
DOMAIN β bu bazaviy tur + ko'p marta ishlatiladigan CHECK qoidasi(lari)ni bitta nomlangan turga "o'rab" qo'yish. Bir CHECK qoidasi loyihada o'nlab ustunda kerak bo'lsa (email, musbat narx, mamlakat kodi) β uni har joyda qaytarib yozish o'rniga, bir marta domen sifatida e'lon qiling.
CREATE DOMAIN email AS text
CHECK (VALUE ~ '^[^@\s]+@[^@\s]+\.[^@\s]+$');
CREATE TABLE foydalanuvchi (
id int PRIMARY KEY,
pochta email NOT NULL -- text emas, "email" domeni
);
INSERT INTO foydalanuvchi VALUES (1, 'oqil@ioqil.uz');
SELECT * FROM foydalanuvchi;
Endi domen qoidasini buzadigan qiymat:
PG18 noto'g'ri emailni rad etdi. Domenning kuchi: email qoidasi foydalanuvchi.pochta, xodim.pochta, mijoz.pochta β hammasida bir xil ishlaydi va qoidani bir joyda o'zgartirsangiz, hamma joyga tatbiq bo'ladi. Bu yaxlitlikni markazlashtiradi va kodni quruq (DRY) qiladi.
Qaror qoidasi: bir xil tekshiruv ikki yoki undan ko'p ustunda takrorlansa β domen. Faqat bitta ustunda bo'lsa β oddiy
CHECKyetarli. (Constraint dizaynini 11-bobda to'liq ko'ramiz.)MySQL'da:
CREATE DOMAINYO'Q. Har ustundaCHECKni qaytarib yozish yoki ilova/trigger'da tekshirish kerak. PostgreSQL domeni β toza dizayn vositasi.
10. Xulosa β tur tanlash cheat-sheet¶
| Maqsad | To'g'ri tur | Qochish kerak |
|---|---|---|
| Pul, narx, balans, foiz | numeric(p,2) |
β float/real/money |
| Taxminiy o'lchov, koordinata | real/double precision |
numeric (sekin, ortiqcha) |
| ID, miqdor (kichik-o'rta) | integer, kalitda bigint |
matn ID |
| Lahza (hodisa qachon) | timestamptz |
β timestamp, text sana |
| Faqat sana | date |
timestamptz (soat ortiqcha) |
| Ha/yo'q | boolean |
int 0/1, char 'Y'/'N' |
| Kichik barqaror to'plam | enum |
erkin text |
| Kengayuvchi/metadatali to'plam | lookup jadval + FK | enum |
| Dinamik, har xil atribut | jsonb + GIN |
butun qatorni JSONB'ga tashlash |
| Doimiy so'raladigan maydon | oddiy ustun | JSONB ichida saqlash |
| Hisoblangan qiymat | generated STORED/VIRTUAL |
qo'lda hisoblab, drift xavfi |
| Kichik element ro'yxati | text[] (ehtiyotkorlik) |
entity bo'lsa β junction jadval |
| Oraliq, ustma-ustlikni taqiqlash | range + EXCLUDE |
ikki ustun (start/end) + kod tekshiruvi |
| Taqsimlangan/mijoz kaliti | uuid (uuidv7() PG18+) |
tasodifiy uuidv4 (indeks shishadi) |
| Takrorlanuvchi tekshiruv | DOMAIN |
har ustunda CHECK takrori |
Eng muhim uchta xulosa: (1) pul β har doim numeric; (2) lahza β har doim timestamptz; (3) JSONB β faqat haqiqatan dinamik qism uchun, sukut bo'yicha tanlov emas.
Mashqlar¶
Oson¶
-
Pul ustuni.
tovarjadvalida narxni saqlash uchun ustun e'lon qiling. So'm tiyinsiz (butun) bo'lsa ham, ertaga aksiyada99.90kerak bo'lishi mumkin. Qaysi tur va nega? Bir qatorlikCREATE TABLEyozing. -
Sana yoki lahza? Quyidagi ustunlarning har biri uchun
date,timestamptzyokitimestampdan birini tanlang va sababini bir jumlada yozing: (a)tugilgan_kun, (b)royxatdan_otgan_vaqt, (c)kunlik_budilnik_vaqti. -
boolean nomi.
is_not_inactive booleanustuni berilgan. Nega bu yomon nom? To'g'ri nom taklif qiling. -
char tuzog'i. Hamkasbingiz mamlakat kodini
char(2)da saqlamoqchi. Bunda qanday taqqoslash tuzog'i bor?textbilan qanday muqobil yozasiz (CHECK bilan)?
O'rta¶
-
Xato turni top. Quyidagi jadvalda KAMIDA 4 ta tur xatosini toping va tuzating:
-
enum yoki lookup? Quyidagi ikki holatning har biri uchun enum yoki lookup jadval tanlang va sababini yozing: (a) buyurtma holati (
yangi/tolangan/.../bekor, kamdan-kam o'zgaradi); (b) blog kategoriyasi (admin istalgan vaqtda yangi kategoriya qo'shadi, har biriga ikona va tavsif kerak). -
JSONB yoki ustun?
foydalanuvchijadvali uchun:email,parol_xesh,royxat_vaqtidoim bor; lekin "profil sozlamalari" (mavzu, til, bildirishnoma yoqilgan-mi, va kelajakda qo'shiladigan noma'lum sozlamalar) har foydalanuvchida har xil. Qaysi maydonlar oddiy ustun, qaysilari JSONB?CREATE TABLEchizing. -
Generated column.
aylanmajadvalidamiqdor intvabirlik_narx numeric(12,2)bor.jamiustunini qo'shing β u har doimmiqdor * birlik_narxga teng bo'lsin va qo'lda yozib bo'lmasin. STORED yoki VIRTUAL β qaysi va nega? -
Domen dizayni. Loyihada
narx,chegirma_narx,yetkazish_narxiustunlari bor β barchasi musbat pul bo'lishi shart. Bu qoidani har joyda qaytarmasdan qanday markazlashtirasiz? Domen e'lon qiling va bitta ustunda ishlating.
Qiyin¶
-
Bron tizimi. Mehmonxona xonalarining bronini modellashtiring: bitta xona bir vaqt oralig'ida faqat bitta bronga ega bo'lishi mumkin (ustma-ust bron TAQIQLANGAN). Qaysi tur va qaysi constraint?
CREATE TABLEyozing va nega ilova kodida emas, bazada tekshirilishini tushuntiring. -
Mahsulot katalogi (aralash). Marketplace'da uch xil mahsulot bor: elektronika (kafolat oyi, quvvat W), kiyim (o'lcham, mato), kitob (muallif, sahifa, ISBN). Hammasida
nom,narx,ombor_qoldigibor. Sxema chizing: qaysi maydonlar barcha mahsulotda umumiy ustun, qaysilari JSONB, vaISBNkabi muhim/noyob qiymatni JSONB ichida bo'lsa ham qanday indekslash/UNIQUE qilish mumkin? -
uuid kaliti. Mobil ilova oflayn rejimda yangi yozuvlar yaratadi va keyin serverga sinxronlaydi. Auto-increment
bigintkalit nega bu yerda muammo? Qaysi turni tanlaysiz va PG18 da qaysi funksiyani DEFAULT qilasiz? Indeks nuqtai nazaridanuuidv4vauuidv7farqini tushuntiring. -
Migratsiya tuzog'i. Loyiha
tolangan_vaqt timestamp(zonasiz) bilan 2 yil ishladi; foydalanuvchilar 3 mamlakatda. Enditimestamptzga o'tmoqchisiz. Nima muammo (eski qiymatlar qaysi zonada edi?), va bu xatoni boshidan oldini olish uchun qanday qoida o'rnatasiz?
Yechimlar¶
Yechim β 1
Pul har doim numeric (decimal), hech qachon float:
CREATE TABLE tovar (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
narx numeric(12,2) NOT NULL CHECK (narx >= 0)
);
numeric(12,2) β 2 ta kasr xona (tiyin) ga joy qoldiradi, shuning uchun 99.90 muammosiz saqlanadi. float bo'lsa 0.1+0.2 tipidagi yaxlitlash xatosi balansga o'tib ketardi. CHECK (narx >= 0) qo'shimcha yaxlitlik beradi.
Yechim β 2
- (a)
tugilgan_kunβdate. Soat/zona ma'nosiz; tug'ilgan kun β sof kalendar sanasi. - (b)
royxatdan_otgan_vaqtβtimestamptz. Bu aniq lahza (hodisa qachon yuz berdi); foydalanuvchilar turli zonada bo'lishi mumkin, UTC saqlab, ko'rsatishda o'girish kerak. - (c)
kunlik_budilnik_vaqtiβtime(yoki zona ataylab muhim bo'lmasa). Bu kun ichidagi vaqt, aniq sana/lahza emas. Agar "mahalliy 09:00" ma'nosida bo'lsa,timeto'g'ri; zona-bog'liq lahza emas.
Yechim β 3
is_not_inactive β bu qo'sh inkor. "Inactive emas" = "active". Kodda WHERE is_not_inactive = false yozsangiz, "active emas" degani bo'lib, miyani chalg'itadi va bug tug'diradi.
To'g'ri: is_active boolean NOT NULL DEFAULT true. Ijobiy, to'g'ridan-to'g'ri o'qiladi: WHERE is_active β "faol bo'lganlar".
Yechim β 4
char(2) tuzog'i: oxiridagi probellar taqqoslashda e'tiborga olinmaydi va saqlashda probel bilan to'ldiriladi. 'UZ ' va 'UZ' "teng" bo'lib chiqishi (yoki aksincha, dasturda kutilmagan probel) chalkashlik tug'diradi. Bobdagi misol: char ustunda c = 'salom' rost, lekin text ustunda a = 'salom ' yolg'on β bu nomuvofiqlik.
Muqobil β text + qat'iy CHECK:
CREATE TABLE mamlakat (
kod text PRIMARY KEY CHECK (kod ~ '^[A-Z]{2}$'), -- aniq 2 ta bosh harf
nomi text NOT NULL
);
CHECK (kod ~ '^[A-Z]{2}$') aniq ikkita bosh harfni majburlaydi va text ning kutilgan, probelsiz taqqoslash xulqini saqlaydi.
Yechim β 5
Beshta tur xatosi va tuzatilgan sxema:
CREATE TABLE tolov (
-- 1. id: varchar(255) o'rniga haqiqiy kalit turi
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
-- 2. summa: float -> numeric (PUL!)
summa numeric(14,2) NOT NULL CHECK (summa >= 0),
-- 3. tolangan_vaqt: timestamp -> timestamptz (lahza, zonali)
tolangan_vaqt timestamptz NOT NULL DEFAULT now(),
-- 4. muvaffaqiyatli: char(1) 'Y'/'N' -> boolean
muvaffaqiyatli boolean NOT NULL DEFAULT false,
-- 5. valyuta: erkin text -> cheklangan to'plam (enum yoki CHECK)
valyuta text NOT NULL CHECK (valyuta IN ('UZS','USD','EUR'))
);
Xatolar: (1) id varchar(255) β kalit matn emas, surrogat bigint bo'lishi kerak; (2) float pul uchun β yaxlitlash xatosi; (3) timestamp zonasiz β lahza uchun timestamptz; (4) char(1) 'Y'/'N' o'rniga haqiqiy boolean; (5) erkin text o'rniga CHECK ... IN (...) (yoki enum) bilan cheklangan to'plam.
Yechim β 6
- (a) Buyurtma holati β enum. Kichik, barqaror, deyarli o'zgarmas to'plam; "kod ichidagi" oqim (yangi β tolangan β ...). Tartiblanishi ham foydali (enum e'lon tartibida saralanadi). Metadata kerak emas.
- (b) Blog kategoriyasi β lookup jadval. Admin runtime'da yangi kategoriya qo'shadi (DDL/migratsiya kerak emas), va har kategoriyaga ikona/tavsif (metadata) kerak β enum buni qila olmaydi.
CREATE TABLE kategoriya (
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
slug text UNIQUE NOT NULL,
nomi text NOT NULL,
ikona text,
tavsif text
);
CREATE TABLE maqola (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
kategoriya_id int NOT NULL REFERENCES kategoriya(id),
sarlavha text NOT NULL
);
Yechim β 7
Doimiy, har foydalanuvchida bor va so'raladigan maydonlar β oddiy ustun. O'zgaruvchan/kelajakda kengayadigan sozlamalar β JSONB:
CREATE TABLE foydalanuvchi (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text UNIQUE NOT NULL,
parol_xesh text NOT NULL,
royxat_vaqti timestamptz NOT NULL DEFAULT now(),
sozlamalar jsonb NOT NULL DEFAULT '{}' -- mavzu, til, bildirishnoma + kelajak
);
email, parol_xesh β har doim bor, filtrlangani uchun (ayniqsa email login'da) oddiy ustun + UNIQUE. royxat_vaqti β lahza, timestamptz. sozlamalar esa har foydalanuvchida har xil va kelajakda kengayadi β JSONB ideal. Agar biror sozlama (masalan til) keng so'ralsa, uni generated column qilib chiqarish mumkin.
Yechim β 8
ALTER TABLE aylanma
ADD COLUMN jami numeric(14,2)
GENERATED ALWAYS AS (miqdor * birlik_narx) STORED;
STORED, chunki: (1) jami ko'pincha hisobotlarda jamlanadi (SUM(jami)) va filtrlanadi β diskda tayyor tursa o'qish tez; (2) uni indekslash kerak bo'lishi mumkin (STORED indekslanadi, VIRTUAL hozircha to'g'ridan-to'g'ri indekslanmaydi). VIRTUAL faqat ifoda arzon va kamdan-kam o'qiladigan, joy muhim bo'lgan holatda yaxshiroq. Pul agregati uchun STORED to'g'ri tanlov. Har ikkala holda ham qiymat avtomatik hisoblanadi β INSERT/UPDATE da jami ga qo'lda yozib bo'lmaydi, shuning uchun drift mumkin emas.
Yechim β 9
Musbat pulni qayta ishlatiluvchi domen sifatida e'lon qiling:
CREATE DOMAIN musbat_pul AS numeric(14,2)
CHECK (VALUE >= 0);
CREATE TABLE buyurtma_qatori (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
narx musbat_pul NOT NULL,
chegirma_narx musbat_pul,
yetkazish_narxi musbat_pul NOT NULL DEFAULT 0
);
Endi narx, chegirma_narx, yetkazish_narxi β uchchovi numeric(14,2) + >= 0 qoidasini avtomatik oladi. Qoidani bir joyda (musbat_pul) o'zgartirsangiz, hamma ustunga tatbiq bo'ladi β DRY va markazlashgan yaxlitlik.
Yechim β 10
tsrange/tstzrange + EXCLUDE constraint:
CREATE EXTENSION IF NOT EXISTS btree_gist; -- "xona WITH =" uchun kerak
CREATE TABLE bron (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
xona int NOT NULL,
davr tstzrange NOT NULL,
EXCLUDE USING gist (xona WITH =, davr WITH &&)
);
EXCLUDE USING gist (xona WITH =, davr WITH &&) baza darajasida "bir xil xona va ustma-ust (&&) davr" juftligini taqiqlaydi. Bobdagi misolda ikkinchi ustma-ust INSERT ERROR: conflicting key value violates exclusion constraint bilan rad etilgan edi.
Nega bazada, ilovada emas: ilovada "avval band-mi tekshir, keyin yoz" qilsangiz, ikki foydalanuvchi bir vaqtda tekshirib, ikkalasi ham "bo'sh" deb ko'rib, ikkovi ham yozishi mumkin (poyga holati / race condition). EXCLUDE constraint atomik va parallel yozishda ham kafolatlaydi β bu "baza himoyasi oxirgi qal'a" tamoyili (11-bob). btree_gist kengaytmasi int ustunni (xona WITH =) GiST indeksida tenglik bilan ishlatish uchun kerak.
Yechim β 11
Umumiy maydonlar β oddiy ustun; toifaga xos atributlar β JSONB; muhim/noyob ISBN β generated column orqali ustunga chiqarib UNIQUE:
CREATE TABLE mahsulot (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nom text NOT NULL,
narx numeric(12,2) NOT NULL CHECK (narx >= 0),
ombor_qoldigi int NOT NULL DEFAULT 0 CHECK (ombor_qoldigi >= 0),
toifa text NOT NULL, -- 'elektronika' | 'kiyim' | 'kitob'
atribut jsonb NOT NULL DEFAULT '{}',
-- ISBN faqat kitoblarda; JSONB'dan ustunga chiqaramiz va UNIQUE qilamiz
isbn text GENERATED ALWAYS AS (atribut->>'isbn') STORED
);
-- JSONB atributlar bo'yicha tez qidiruv
CREATE INDEX idx_mahsulot_atribut ON mahsulot USING gin (atribut);
-- ISBN noyob bo'lsin (NULL'lar ko'p bo'lishi mumkin -> qisman UNIQUE indeks)
CREATE UNIQUE INDEX uq_mahsulot_isbn ON mahsulot (isbn) WHERE isbn IS NOT NULL;
nom, narx, ombor_qoldigi β barcha mahsulotda bor, doim so'raladi β oddiy ustun. Toifaga xos maydonlar (kafolat_oyi, o'lcham, material, sahifa...) har xil β atribut jsonb. ISBN muhim va noyob bo'lishi kerak, lekin faqat kitobda bor β uni STORED generated column qilib JSONB'dan chiqaramiz, so'ng WHERE isbn IS NOT NULL qisman UNIQUE indeks bilan faqat mavjud ISBN'lar noyobligini majburlaymiz (kitob bo'lmaganlarda NULL, ular cheklovga tushmaydi).
Yechim β 12
Auto-increment muammosi: oflayn ikki qurilma bir xil navbatdagi raqamni (masalan 1043) yaratib qo'yishi mumkin, chunki ular serverga ulanmagan holda mahalliy hisoblagichdan o'qiydi. Sinxronlashda kalit to'qnashuvi yoki qayta raqamlash kerak bo'ladi β keyingi FK'lar buziladi.
Yechim: uuid kalit, har qurilma o'zi to'qnashuvsiz yarata oladi:
CREATE TABLE yozuv (
id uuid PRIMARY KEY DEFAULT uuidv7(), -- PostgreSQL 18+
matn text NOT NULL,
yaratilgan timestamptz NOT NULL DEFAULT now()
);
uuidv4 vs uuidv7 indeks farqi: uuidv4 to'liq tasodifiy β yangi qatorlar B-tree indeksning tasodifiy joylariga tushadi, bu sahifa bo'linishini ko'paytiradi va indeksni shishiradi (yozish sekinlashadi, kesh samarasi tushadi). uuidv7 ichida vaqt belgisi bor β yangi qatorlar indeksning oxiriga ketma-ket tushadi (xuddi auto-increment kabi), shuning uchun indeks zich va yozish samaraliroq. Shu sabab taqsimlangan tizimda PG18 da uuidv7() afzal.
Yechim β 13
Muammo: timestamp (zonasiz) eski qiymatlar "naive" β ularda zona yozilmagan. Foydalanuvchilar 3 mamlakatda bo'lsa, 2024-03-15 14:00 qiymati Toshkent vaqtimi, Moskva vaqtimi yoki UTCmi β hech kim ishonch bilan ayta olmaydi. timestamptz ga AT TIME ZONE bilan o'tkazganda qaysi zonani tanlash kerakligi noaniq; noto'g'ri taxmin qilsangiz, hamma eski yozuv soatlarga siljiydi (masalan UTC+5 deb hisoblasangiz, lekin aslida UTC bo'lsa, 5 soat xato).
Amaliy yondashuv: ma'lumot qaysi zonada yozilganini (log, ilova sozlamasi, server zonasi) aniqlab, shu zona bilan column AT TIME ZONE 'Asia/Tashkent' qilib timestamptz ga o'tkazish β lekin bu taxmin doim biroz xavfli.
Boshidan oldini olish qoidasi: loyihaning birinchi kunidan barcha lahza ustunlari timestamptz bo'lsin. Komanda standarti: "hodisa vaqti = har doim timestamptz, sukut bo'yicha DEFAULT now()". Linter/code-review da timestamp without time zone ishlatilishini taqiqlang. Shunda bu og'riqli, ma'lumot-buzuvchi migratsiya hech qachon kerak bo'lmaydi.
β¬ οΈ Oldingi: 09 β Logik modeldan fizik sxemaga Β· π README Β· Keyingi: 11 β Yaxlitlik va constraint dizayni β‘οΈ