Ma'lumot turlari — INTEGER, SERIAL, TEXT, BOOLEAN, TIMESTAMP, UUID, ARRAY, JSONB

~25 daq · PostgreSQL · Kirish va O'rnatish

Muammodan boshlaylik

Jadval yaratmoqchisiz. Foydalanuvchi uchun quyidagi ma'lumotlar kerak:

- id
- ism
- telefon raqami
- yosh
- balans (hisobdagi pul)
- faolmi yoki yo'qmi
- ro'yxatdan o'tgan vaqt

Har bir maydon uchun qaysi turni tanlash kerak? Noto'g'ri tanlasangiz:

  • Telefon raqamini INTEGER da saqlasangiz — +998901234567 sig'maydi, boshidagi 0 yo'qoladi
  • Pulni FLOAT da saqlasangiz — 100.10 + 200.20 = 300.3000000000003 — yaxlitlash xatosi
  • Hamma narsani TEXT qilsangiz — yosh ga "yigirma besh" yoziladi, hisoblash bo'lmaydi

To'g'ri tur tanlash — database ning asosi.


Ma'lumot turi nima va nima uchun kerak?

Har bir ustun bitta turga ega. Bu tur:

  • qanday ma'lumot saqlanishini belgilaydi
  • noto'g'ri ma'lumot kiritilsa — database xato beradi
  • diskda qancha joy egallashini aniqlaydi
  • qanday hisob-kitob va qidiruv qilish mumkinligini ko'rsatadi

Hayotiy o'xshatma

Ofis jildlari. Har bir jild faqat ma'lum hujjat uchun: pasport jildi, shartnoma jildi, to'lov jildi. Pasport jildiga shartnoma solsangiz — tartib buziladi, kerak paytda topib bo'lmaydi. Ma'lumot turlari ham xuddi shunday — har bir ma'lumot o'z "jildida" saqlanadi.


Sonlar

INTEGER — butun son

Kundalik ishda eng ko'p ishlatiladigan son turi.

SMALLINT   — -32,768 dan 32,767 gacha        (2 bayt)
INTEGER    — -2,147,483,648 dan 2,147,483,647 (4 bayt)  ← eng ko'p ishlatiladigan
BIGINT     — -9,223,372,036,854,775,808 dan …  (8 bayt)
yosh        INTEGER   -- 0 dan 150 gacha yetarli
mahsulot_soni INTEGER  -- oddiy miqdor
ko'rishlar  BIGINT    -- katta YouTube kanali: milliardan oshishi mumkin

Nima uchun BIGINT ni hamma joyda ishlatmaslik kerak? U INTEGER dan ikki barobar ko'p joy egallaydi. Millionlab qatorda bu sezilarli farq.

SERIAL — avtomatik o'sadigan son

id ustuni uchun. Har yangi qator qo'shilganda qiymat o'zi oshadi.

SERIAL      — 1 dan 2,147,483,647 gacha   (INTEGER asosida)
BIGSERIAL   — 1 dan 9,223,372,036,854,775,807 gacha (BIGINT asosida)
CREATE TABLE foydalanuvchi (
    id    SERIAL PRIMARY KEY,  -- ← 1, 2, 3, 4 ... o'zi ketadi
    ism   TEXT
);

INSERT INTO foydalanuvchi (ism) VALUES ('Ali');   -- id = 1 o'zi beriladi
INSERT INTO foydalanuvchi (ism) VALUES ('Vali');  -- id = 2

NUMERIC — aniq kasr son (pul uchun)

FLOAT va REAL — taxminiy qiymat saqlaydi, kichik xatolar bo'lishi mumkin. Pul uchun bu qabul qilinmas.

SELECT 0.1 + 0.2;
-- FLOAT:   0.30000000000000004  ← xato!
-- NUMERIC: 0.3                  ← to'g'ri

NUMERIC(aniqlik, kasr) — jami raqam soni va kasr qismi:

narx     NUMERIC(10, 2)  -- 99999999.99 gacha, 2 ta kasr
foiz     NUMERIC(5, 2)   -- 999.99 gacha
balans NUMERIC(15, 2)  -- pul uchun har doim NUMERIC

Matn

TEXT — cheksiz matn

Eng qulay matn turi. Uzunlik chegarasi yo'q.

ism          TEXT
tavsif       TEXT
xabar_matni  TEXT

VARCHAR(n) — maksimal uzunlik bilan matn

telefon  VARCHAR(20)   -- +998901234567 — 13 belgi, 20 yetarli
kod      VARCHAR(6)    -- SMS kod: 123456

Nima uchun VARCHAR ni hamma joyga ishlatmaslik kerak? PostgreSQL da TEXT va VARCHAR bir xil tezlikda ishlaydi — VARCHAR ni faqat chegaralash mantiqan kerak bo'lganda ishlating, tezlik uchun emas.

CHAR(n) — qat'iy uzunlik

Har doim n belgidan iborat bo'ladi — qisqa bo'lsa bo'sh joy bilan to'ldiriladi.

davlat_kodi  CHAR(2)   -- "UZ", "US", "DE" — har doim 2 belgi

BOOLEAN — ha yoki yo'q

Faqat ikki qiymat: TRUE yoki FALSE.

faol          BOOLEAN DEFAULT TRUE
email_tasdiqlangan BOOLEAN DEFAULT FALSE
SELECT * FROM foydalanuvchi WHERE faol = TRUE;
-- yoki qisqaroq:
SELECT * FROM foydalanuvchi WHERE faol;

Vaqt va sana

DATE — faqat sana

tug'ilgan_kun   DATE   -- 1995-03-15

TIME — faqat vaqt

ish_boshlanishi TIME   -- 09:00:00

TIMESTAMP — sana va vaqt

yaratilgan_vaqt TIMESTAMP   -- 2024-03-15 14:30:00

TIMESTAMPTZ — vaqt mintaqasi bilan

TIMESTAMP   — "2024-03-15 14:30:00" — vaqt mintaqasini bilmaydi
TIMESTAMPTZ — "2024-03-15 14:30:00+05" — Toshkent vaqti, xalqaro loyihada to'g'ri

Nima uchun TIMESTAMPTZ ishlatish kerak? Foydalanuvchilar turli mamlakatda bo'lsa — Toshkentdagi "14:30" Moskvada "12:30", Londonda "09:30". TIMESTAMPTZ barchasini UTC da saqlaydi, kerak vaqtda har qanday mintaqaga o'giradi.

yaratilgan_vaqt  TIMESTAMPTZ DEFAULT NOW()  -- ← hozirgi vaqt avtomatik
yangilangan_vaqt TIMESTAMPTZ
┌─────────────┬────────────────────────────────────────────┐
│     Tur     │                  Ishlatish                 │
├─────────────┼────────────────────────────────────────────┤
│ DATE        │ Tug'ilgan kun, bayram — vaqt kerak emas    │
│ TIME        │ Ish grafigi — sana kerak emas              │
│ TIMESTAMP   │ Faqat bitta mintaqali tizim                │
│ TIMESTAMPTZ │ Xalqaro loyiha, turli vaqt mintaqalari     │
└─────────────┴────────────────────────────────────────────┘

UUID — noyob identifikator

UUID — 128 bitli noyob qiymat. Ko'rinishi:

a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11

SERIAL dan farqi nima?

SERIAL: 1, 2, 3, 4 ...
UUID:   a0eebc99-..., f47ac10b-..., 6ba7b810-...

SERIAL — foydalanuvchi URL da ?id=5 ko'rsa, ?id=6 ni sinab ko'rishi mumkin. UUID bilan bu imkonsiz. Bundan tashqari bir nechta server parallel ishlasa SERIAL ning 1, 2, 3 qiymatlari to'qnashishi mumkin, UUID to'qnashmaydi.

CREATE TABLE foydalanuvchi (
    id   UUID DEFAULT gen_random_uuid() PRIMARY KEY,  -- ← o'zi generatsiya qiladi
    ism  TEXT
);

ARRAY — massiv

Bitta ustunda bir nechta qiymat saqlash:

CREATE TABLE mahsulot (
    id       SERIAL PRIMARY KEY,
    nom      TEXT,
    rasmlar  TEXT[],      -- ← matn massivi
    teglar   TEXT[]
);

INSERT INTO mahsulot (nom, rasmlar, teglar)
VALUES (
    'Noutbuk',
    ARRAY['rasm1.jpg', 'rasm2.jpg', 'rasm3.jpg'],
    ARRAY['elektronika', 'kompyuter']
);

Massivdan qidirish:

SELECT * FROM mahsulot
WHERE 'elektronika' = ANY(teglar);  -- ← massiv ichida qidirish

Nima uchun ARRAY ehtiyotkorlik bilan ishlatiladi? Massiv ichidagi elementlarga JOIN qilib bo'lmaydi — ular alohida jadval emas. Ko'p-ko'p munosabat kerak bo'lsa — alohida jadval yaxshiroq.


JSONB — JSON ma'lumot

Tuzilmasi oldindan aniq bo'lmagan ma'lumotlar uchun. Har bir foydalanuvchining profil sozlamalari har xil bo'lishi mumkin:

CREATE TABLE foydalanuvchi (
    id       SERIAL PRIMARY KEY,
    ism      TEXT,
    sozlamalar JSONB   -- ← har kim uchun har xil bo'lishi mumkin
);

INSERT INTO foydalanuvchi (ism, sozlamalar) VALUES
('Ali',  '{"til": "uz", "bildirishnoma": true}'),
('Vali', '{"til": "ru", "bildirishnoma": false, "mavzu": "qorong'"'"'i"}');

JSONB ichidan ma'lumot olish:

SELECT ism, sozlamalar->>'til' AS til
FROM foydalanuvchi;

--  ism  │  til
-- ──────┼──────
--  Ali  │ uz
--  Vali │ ru

JSON va JSONB farqi:

JSON  — matnni xuddi shunday saqlaydi, o'qiyotganda tahlil qiladi
JSONB — ikkilik formatda saqlaydi, indekslanadi, qidiruv tez

Har doim JSONB ishlating — JSON ni faqat asl yozuvni aynan saqlash kerak bo'lsa ishlating.


Qaysi turni tanlash — umumiy qoida

┌─────────────────────────┬──────────────────────────────┐
│       Ma'lumot          │             Tur              │
├─────────────────────────┼──────────────────────────────┤
│ id (avtomatik)          │ SERIAL yoki UUID             │
│ Ism, email, matn        │ TEXT                         │
│ Telefon, kod            │ VARCHAR(n)                   │
│ Yosh, miqdor, soni      │ INTEGER                      │
│ Katta son (views, like) │ BIGINT                       │
│ Pul, narx, foiz         │ NUMERIC(10, 2)               │
│ Ha/Yo'q                 │ BOOLEAN                      │
│ Sana                    │ DATE                         │
│ Vaqt muhim (log, audit) │ TIMESTAMPTZ                  │
│ Noyob ID (xavfsiz)      │ UUID                         │
│ Rasmlar, teglar ro'yxat │ TEXT[] yoki alohida jadval   │
│ O'zgaruvchan tuzilma    │ JSONB                        │
└─────────────────────────┴──────────────────────────────┘

Tez-tez uchraydigan xatolar

Telefon raqamini INTEGER da saqlash

+998901234567 — 13 ta belgi, INTEGER sig'ishi qiyin. Boshidagi 0 yo'qoladi — 0901234567 o'rniga 901234567 saqlanadi. Telefon raqami — matn, hisob-kitob qilinmaydi:

-- ❌
telefon INTEGER

-- ✅
telefon VARCHAR(20)

Pulni FLOAT da saqlash

-- ❌
narx FLOAT   -- 100.10 + 200.20 = 300.3000000000003

-- ✅
narx NUMERIC(10, 2)  -- 100.10 + 200.20 = 300.30

Hamma joyda SERIAL, UUID kerak joyda SERIAL

Tashqi tizimlar bilan integratsiya, API, URL da id ko'rinadigan bo'lsa — UUID xavfsizroq. Faqat ichki jadvallar uchun SERIAL yetarli.

TIMESTAMP o'rniga TIMESTAMPTZ ishlatmaslik

Keyinchalik xalqaro foydalanuvchilar qo'shilsa — barcha vaqtlarni qayta hisoblash kerak bo'ladi. Boshidanoq TIMESTAMPTZ ishlating.

VARCHAR uzunligini kichik qo'yish

-- ❌ — email 255 belgigacha bo'lishi mumkin
email VARCHAR(50)

-- ✅
email TEXT  -- yoki VARCHAR(255)

Xulosa

To'g'ri tur tanlash — ma'lumot sifati va tezligining asosi.

Asosiy qoidalar:

  • Pul → NUMERIC(10, 2), hech qachon FLOAT
  • Telefon → VARCHAR(20), hech qachon INTEGER
  • Vaqt → TIMESTAMPTZ, xalqaro loyihada majburiy
  • id → SERIAL (oddiy) yoki UUID (xavfsiz, tashqi integratsiya)
  • O'zgaruvchan tuzilma → JSONB, hech qachon JSON

Keyingi dars

5-dars: Schema — nima, yaratish, search_path, public schema