ARRAY — yaratish, operatorlar, ANY, ALL, unnest

~20 daq · PostgreSQL · PostgreSQL Maxsus Imkoniyatlari

Muammodan boshlaylik

Foydalanuvchilar tizimida har bir foydalanuvchining bir nechta teli raqami bo'lishi mumkin. Yoki mahsulotning bir nechta tegi bor.

Oddiy yechim — alohida jadval: telefon_raqamlar(foydalanuvchi_id, raqam). To'g'ri yechim — lekin har bir qo'shimcha so'rov uchun JOIN kerak.

Ba'zan ma'lumot shunchaki sodda ro'yxat — murakkab munosabat emas. Masalan, teglar, rasmlar ro'yxati, ruxsat kodlari. Bunday holat uchun PostgreSQL da ARRAY turi bor.


ARRAY nima?

ARRAY — bir xil turdagi qiymatlar to'plami, bitta ustunda saqlanadi. Har bir element indeks orqali olinadi.

Hayotiy o'xshatma: xarid qilish ro'yxati. Daftarning bitta satrida bir nechta mahsulot yozilgan: "non, sut, tuxum". Bu satr — array. Alohida jadvalga chiqarish kerak emas — shunchaki bitta joyda ro'yxat.

Alohida jadval:              ARRAY:
────────────────────         ────────────────────────────
user_id │ telefon             user_id │ telefonlar
────────┼─────────            ────────┼─────────────────────
1       │ 901234567           1       │ {901234567, 712345678}
1       │ 712345678
                              JOIN kerak emas ✅
JOIN kerak, 2 qator           1 qator ✅

ARRAY ustun yaratish

Har qanday turga [] qo'shilsa — array:

CREATE TABLE foydalanuvchilar (
  id        SERIAL PRIMARY KEY,
  ism       TEXT,
  telefonlar TEXT[],           -- ← TEXT array
  teglar     TEXT[],           -- ← TEXT array
  baholar    INTEGER[]         -- ← INTEGER array
);

Ma'lumot qo'shish

INSERT INTO foydalanuvchilar (ism, telefonlar, teglar, baholar) VALUES
  ('Ali',   ARRAY['901234567', '712345678'], ARRAY['vip', 'aktiv'],      ARRAY[85, 90, 78]),
  ('Vali',  ARRAY['935678901'],              ARRAY['yangi'],              ARRAY[70]),
  ('Soli',  ARRAY['998765432', '901112233'], ARRAY['vip', 'premium'],    ARRAY[95, 88, 92]),
  ('Jamol', NULL,                            ARRAY['aktiv'],              ARRAY[60, 75]);

Yoki {} sintaksisi bilan:

INSERT INTO foydalanuvchilar (ism, telefonlar) VALUES
  ('Kamol', '{"998001122", "712334455"}');

Elementlarni o'qish

PostgreSQL array indeksi 1 dan boshlanadi — boshqa ko'pgina tillardan farqli:

SELECT
  ism,
  telefonlar,            -- barcha elementlar
  telefonlar[1],         -- birinchi element
  telefonlar[2]          -- ikkinchi element
FROM foydalanuvchilar;
  ism   │      telefonlar        │ telefonlar[1] │ telefonlar[2]
--------+------------------------+---------------+---------------
 Ali    │ {901234567,712345678}  │ 901234567     │ 712345678
 Vali   │ {935678901}            │ 935678901     │ NULL
 Soli   │ {998765432,901112233}  │ 998765432     │ 901112233
 Jamol  │ NULL                   │ NULL          │ NULL

Kesim (slice) — bir nechta element

SELECT baholar[1:2] FROM foydalanuvchilar WHERE ism = 'Soli';
-- {95, 88}  ← 1-dan 2-gacha

Array uzunligi va elementlar soni

SELECT
  ism,
  array_length(telefonlar, 1) AS telefon_soni,
  -- 1 → birinchi o'lcham (ko'p o'lchovli array uchun 2, 3...)
  cardinality(teglar)         AS teg_soni
  -- cardinality — NULL ni ham hisobga olgan array_length
FROM foydalanuvchilar;
  ism   │ telefon_soni │ teg_soni
--------+--------------+----------
 Ali    │ 2            │ 2
 Vali   │ 1            │ 1
 Soli   │ 2            │ 2
 Jamol  │ NULL         │ 1

Filtrlash operatorlari

ANY — elementlardan biri teng

"Teglar ichida 'vip' bor foydalanuvchilar":

SELECT ism FROM foydalanuvchilar
WHERE 'vip' = ANY(teglar);
 ism
------
 Ali
 Soli

ANY(massiv) — massivning birorta elementi shartga mos kelsa rost. = ANY — IN ga o'xshash, lekin array ustun uchun.

ALL — barcha elementlar teng

"Barcha baholari 80 dan yuqori":

SELECT ism FROM foydalanuvchilar
WHERE 80 < ALL(baholar);
 ism
------
 Ali
 Soli

ALL(massiv) — massivning barcha elementlari shartga mos kelsa rost.

@> — ichida bor

Array ichida berilgan array to'liq bormi:

-- 'vip' va 'aktiv' ikkalasi ham bor foydalanuvchilar
SELECT ism FROM foydalanuvchilar
WHERE teglar @> ARRAY['vip', 'aktiv'];
 ism
-----
 Ali

Soli da 'vip' bor lekin 'aktiv' yo'q — tushib qoldi.

&& — kesishma bormi

Ikki arraydan birorta umumiy element bormi:

-- 'premium' yoki 'vip' tegi bor foydalanuvchilar
SELECT ism FROM foydalanuvchilar
WHERE teglar && ARRAY['premium', 'vip'];
 ism
------
 Ali
 Soli

Array o'zgartirish

Element qo'shish — ||

-- Oxiriga qo'shish
UPDATE foydalanuvchilar
SET teglar = teglar || ARRAY['tasdiqlangan']
WHERE ism = 'Vali';

-- Boshiga qo'shish
UPDATE foydalanuvchilar
SET teglar = ARRAY['super'] || teglar
WHERE ism = 'Ali';

Element o'chirish — array_remove

UPDATE foydalanuvchilar
SET teglar = array_remove(teglar, 'yangi')
WHERE ism = 'Vali';
-- 'yangi' tegi o'chirildi

Almashtirish — array_replace

UPDATE foydalanuvchilar
SET teglar = array_replace(teglar, 'aktiv', 'faol')
WHERE ism = 'Ali';
-- 'aktiv' → 'faol' ga almashdi

unnest — arrayni qatorlarga yoyish

Array elementlarini alohida qatorlar sifatida ko'rsatish:

SELECT ism, unnest(teglar) AS teg
FROM foydalanuvchilar;
  ism   │    teg
--------+-----------
 Ali    │ vip
 Ali    │ aktiv
 Vali   │ yangi
 Soli   │ vip
 Soli   │ premium
 Jamol  │ aktiv

Har bir element alohida qator bo'ldi. Bu — LATERAL bilan birga ayniqsa kuchli.

unnest bilan raqamlash

SELECT
  ism,
  ordinality AS tartib,
  teg
FROM foydalanuvchilar,
     unnest(teglar) WITH ORDINALITY AS t(teg, ordinality);
  ism   │ tartib │    teg
--------+--------+-----------
 Ali    │ 1      │ vip
 Ali    │ 2      │ aktiv
 Vali   │ 1      │ yangi
 Soli   │ 1      │ vip
 Soli   │ 2      │ premium

WITH ORDINALITY — har elementga tartib raqami beradi.


array_agg — qatorlarni arrayga yig'ish

unnest ning aksi — alohida qatorlardan array yaratish:

-- Har shahar uchun foydalanuvchilar ro'yxati
SELECT
  shahar,
  array_agg(ism ORDER BY ism) AS foydalanuvchilar
FROM foydalanuvchilar
GROUP BY shahar;
   shahar   │     foydalanuvchilar
------------+------------------------
 Toshkent   │ {Ali, Jamol, Soli}
 Samarqand  │ {Vali}

Tez-tez uchraydigan xatolar

Indeks 0 dan boshlanadi deb o'ylash:

-- ❌ — PostgreSQL da array indeksi 1 dan boshlanadi
SELECT telefonlar[0] FROM foydalanuvchilar;
-- NULL qaytadi — xato emas, lekin noto'g'ri

-- ✅
SELECT telefonlar[1] FROM foydalanuvchilar;

Boshqa tillarda (Python, JS) 0 dan boshlanadi — PostgreSQL da 1 dan. Bu ko'p adashtiradi.

NULL array va bo'sh array farqini bilmaslik:

-- NULL array — umuman qiymat yo'q
-- Bo'sh array — qiymat bor, lekin elementlar yo'q

SELECT array_length(NULL::TEXT[], 1);   -- NULL
SELECT array_length(ARRAY[]::TEXT[], 1); -- NULL (PostgreSQL da bo'sh array uzunligi NULL)
SELECT cardinality(ARRAY[]::TEXT[]);     -- 0

cardinality bo'sh array uchun 0 qaytaradi — array_length esa NULL. Nol elementni tekshirishda cardinality ishlatgan ma'qul.


Xulosa

ARRAY — bitta ustunda bir xil turdagi qiymatlar ro'yxati. Kichik, sodda ro'yxatlar uchun alohida jadval va JOIN kerak emas.

telefonlar[1]      → birinchi element (1 dan boshlanadi!)
ANY(massiv)        → elementlardan biri shartga mos
ALL(massiv)        → barcha elementlar shartga mos
@>                 → array ichida boshqa array bor
&&                 → umumiy element bor
unnest             → arrayni qatorlarga yoy
array_agg          → qatorlarni arrayga yig'

Qachon ARRAY, qachon alohida jadval:

ARRAY:              Alohida jadval:
──────────────      ──────────────────
Sodda ro'yxat       Murakkab munosabat
Tartib muhim        Qo'shimcha ustunlar kerak
Ko'p JOIN noqulay   Katta hajmdagi ma'lumot
Teglar, ruxsatlar   Pul operatsiyalari, buyurtmalar

Keyingi dars

25-dars: Full-text Search — tsvector, tsquery, to_tsvector, plainto_tsquery