UNION, INTERSECT, EXCEPT

~15 daq · PostgreSQL · Ilg'or SQL

Muammodan boshlaylik

Ikki alohida jadvalda ma'lumot bor: faol_mijozlar va arxiv_mijozlar. Barchasini birga ko'rsatish kerak — bitta ro'yxatda. JOIN ishlamaydi, chunki bir-biriga bog'liq emas, shunchaki bir xil tuzilmadagi ikki jadval.

Yoki boshqa holat: yangi ro'yxatdan eski ro'yxatda ham bor bo'lganlarni topish kerak. Yoki aksincha — yangida bor, eskida yo'q.

Bu uchala muammo uchun — to'plam operatorlari: UNION, INTERSECT, EXCEPT.


To'plam operatorlari nima?

To'plam operatorlari ikki so'rovning natijalarini birlashtiradi, kesishini yoki farqini topadi. Matematikadagi to'plamlar nazariyasiga o'xshash.

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

UNION       = {1, 2, 3, 4, 5, 6}   ← ikkalasini birlashtir
INTERSECT   = {3, 4}                ← faqat ikkalasida borlar
EXCEPT      = {1, 2}                ← A da bor, B da yo'q

Vizual:

    A         B              UNION
  ┌───┐     ┌───┐          ┌───────┐
  │1,2│  3,4│   │    →     │1,2,3,4│
  │   ├─────┤5,6│          │  5,6  │
  └───┘     └───┘          └───────┘

              INTERSECT          EXCEPT (A - B)
              ┌───┐              ┌───┐
              │3,4│              │1,2│
              └───┘              └───┘

Shartlar

To'plam operatorlari ishlashi uchun ikki so'rov:

  1. Bir xil ustunlar soni bo'lishi kerak
  2. Ustunlar turlari mos kelishi kerak
  3. Natijada ustun nomlari birinchi so'rovdan olinadi

Namuna jadvallar

CREATE TABLE eski_mijozlar (
  ism   TEXT,
  email TEXT
);

CREATE TABLE yangi_mijozlar (
  ism   TEXT,
  email TEXT
);

INSERT INTO eski_mijozlar VALUES
  ('Ali',   'ali@mail.com'),
  ('Vali',  'vali@mail.com'),
  ('Soli',  'soli@mail.com');

INSERT INTO yangi_mijozlar VALUES
  ('Soli',  'soli@mail.com'),
  ('Kamol', 'kamol@mail.com'),
  ('Jasur', 'jasur@mail.com');

UNION — birlashtirish

Ikki so'rov natijasini birlashtiradi. Takroriy qatorlarni avtomatik o'chiradi.

SELECT ism, email FROM eski_mijozlar
UNION
SELECT ism, email FROM yangi_mijozlar;
  ism   │     email
--------+----------------
 Ali    │ ali@mail.com
 Vali   │ vali@mail.com
 Soli   │ soli@mail.com    ← ikkala jadvalda bor, lekin bir marta chiqdi
 Kamol  │ kamol@mail.com
 Jasur  │ jasur@mail.com

Soli ikki jadvalda ham bor — lekin UNION takrorni o'chirdi, bir marta chiqdi.

UNION ALL — takrorlarni saqlab qoldirish

Agar takrorlar kerak bo'lsa yoki tezroq ishlashi kerak bo'lsa:

SELECT ism, email FROM eski_mijozlar
UNION ALL
SELECT ism, email FROM yangi_mijozlar;
  ism   │     email
--------+----------------
 Ali    │ ali@mail.com
 Vali   │ vali@mail.com
 Soli   │ soli@mail.com
 Soli   │ soli@mail.com    ← ikki marta chiqdi
 Kamol  │ kamol@mail.com
 Jasur  │ jasur@mail.com

UNION takrorlarni o'chirish uchun barcha qatorlarni solishtiradi — bu sekinroq. UNION ALL shunchaki qatorlarni qo'shib ketadi — tezroq. Takror bo'lmasligini bilsangiz — UNION ALL ishlating.


INTERSECT — kesishma

Faqat ikkala so'rovda ham bor qatorlarni qaytaradi.

SELECT ism, email FROM eski_mijozlar
INTERSECT
SELECT ism, email FROM yangi_mijozlar;
  ism  │     email
-------+---------------
 Soli  │ soli@mail.com

Faqat Soli ikkala jadvalda ham bor. Ali va Vali faqat eskida, Kamol va Jasur faqat yangida.

Amalda ishlatilishi: ikki ro'yxatda ham borlarni topish — masalan, ikkala kampaniyaga ham yozilgan foydalanuvchilar.


EXCEPT — farq

Birinchi so'rovda bor, lekin ikkinchisida yo'q qatorlarni qaytaradi.

-- Eski mijozlar, yangilarda yo'qlar
SELECT ism, email FROM eski_mijozlar
EXCEPT
SELECT ism, email FROM yangi_mijozlar;
  ism  │     email
-------+---------------
 Ali   │ ali@mail.com
 Vali  │ vali@mail.com

Ali va Vali faqat eski jadvalda bor. Soli ikkisida ham bor — EXCEPT uni o'chirdi.

Tartib muhim: A EXCEPT B ≠ B EXCEPT A.

-- Yangi mijozlar, eskilarda yo'qlar
SELECT ism, email FROM yangi_mijozlar
EXCEPT
SELECT ism, email FROM eski_mijozlar;
  ism   │     email
--------+----------------
 Kamol  │ kamol@mail.com
 Jasur  │ jasur@mail.com

Amaliy misol — turli jadvallarni birlashtirish

Hisobot uchun barcha moliya operatsiyalarini bitta ro'yxatda ko'rsatish:

SELECT 'Kirim' AS tur, sana, summa FROM kirimlar
UNION ALL
SELECT 'Chiqim',       sana, summa FROM chiqimlar
ORDER BY sana;

Bu yerda birinchi so'rovda 'Kirim' — qo'lda qo'shilgan ustun. Natijada har bir qatorda operatsiya turi ko'rinadi.


ORDER BY va LIMIT

To'plam operatori bilan ORDER BY va LIMIT faqat eng oxirida — butun natijaga qo'llanadi:

SELECT ism FROM eski_mijozlar
UNION
SELECT ism FROM yangi_mijozlar
ORDER BY ism       -- ← butun natijani tartiblaydi
LIMIT 3;           -- ← butun natijadan faqat 3 ta

Har bir qism so'rovga alohida ORDER BY qo'yib bo'lmaydi — mantiqsiz, chunki natijalar birlashtirilib ketadi.


Tez-tez uchraydigan xatolar

Ustunlar soni mos kelmasa:

-- ❌ — birinchida 2 ustun, ikkinchida 3
SELECT ism, email FROM eski_mijozlar
UNION
SELECT ism, email, telefon FROM yangi_mijozlar;
-- ERROR: each UNION query must have the same number of columns

Ustunlar soni har ikki so'rovda bir xil bo'lishi shart. Agar biridagisi kam bo'lsa — NULL bilan to'ldirish:

SELECT ism, email, NULL AS telefon FROM eski_mijozlar
UNION
SELECT ism, email, telefon          FROM yangi_mijozlar;

UNION o'rniga UNION ALL ishlatish kerak bo'lganda:

-- ❌ — takrorlar kerak bo'lgan holda UNION ishlatildi
-- Natijada ba'zi qatorlar yo'qoldi, raqam noto'g'ri chiqdi
SELECT mahsulot FROM bugungi_buyurtmalar
UNION
SELECT mahsulot FROM kechagi_buyurtmalar;

-- ✅ — barcha qatorlar kerak, takror bo'lsa ham
SELECT mahsulot FROM bugungi_buyurtmalar
UNION ALL
SELECT mahsulot FROM kechagi_buyurtmalar;

Hisobotlarda UNION ishlatib, takroriy qatorlar o'chirilsa — natijalar noto'g'ri bo'lib qoladi. Takrorlar kerakmi yo'qmi — avval o'ylab ko'ring.


Xulosa

To'plam operatorlari ikki so'rov natijasini birlashtiradi — jadvallarni emas, natijalarni.

UNION       → ikkalasini birlashtir, takrorlarni o'chir
UNION ALL   → ikkalasini birlashtir, tezroq, takrorlar saqlanadi
INTERSECT   → faqat ikkalasida borlar
EXCEPT      → birinchida bor, ikkinchisida yo'q

Shartlar: bir xil ustunlar soni, mos turlar. Ustun nomlari birinchi so'rovdan.

ORDER BY va LIMIT — faqat eng oxirida, butun natijaga.


Keyingi dars

21-dars: LATERAL JOIN — nima, qachon kerak