PostgreSQL / FULL OUTER JOIN ба CROSS JOIN

FULL OUTER JOIN ба CROSS JOIN

Join-уудын ангиллаа дуусгая. FULL OUTER JOIN нь хоёр хүснэгтийн бүгдийг нэгэн зэрэг харуулдаг — аль нэг талд таарахгүй байсан ч. CROSS JOIN нь хоёр хүснэгтийн бүх мөрийн бүх хослолыг үүсгэдэг. Хоёулаа тусгай тохиолдолд маш хэрэгтэй.

FULL OUTER JOIN — хоёр талын бүгдийг хадгална

LEFT JOIN зүүнийг, RIGHT JOIN баруунийг хамгаалдаг байсан бол FULL OUTER JOIN хоёуланг хамгаалдаг. Аль хүснэгтэд таарахгүй байсан ч бичлэг үр дүнд орно — таарахгүй тал NULL болно.

sql
SELECT
    a.нэр     AS зохиогч,
    p.гарчиг  AS нийтлэл
FROM authors AS a
FULL OUTER JOIN posts AS p ON p.author_id = a.id;

Хэрэв authors-т Ганбаяр нийтлэлгүй, posts-т зохиогчгүй нийтлэл байвал:

код
  зохиогч    |        нийтлэл
-------------+----------------------
 Болд         | SQL суурь
 Болд         | JOIN тайлбар
 Сарнай       | Index гэж юу вэ
 Ганбаяр      | NULL               ← зохиогч байгаа, нийтлэл байхгүй
 NULL         | Эзэнгүй нийтлэл    ← нийтлэл байгаа, зохиогч байхгүй

FULL OUTER JOIN-ны хэрэглээ

Хоёр өгөгдлийн эх үүсвэрийн ялгааг харах — жишээ нь хуучин ба шинэ системийн мэдээллийг харьцуулах:

sql
-- Хуучин ба шинэ хэрэглэгчийн жагсаалтыг харьцуулах
SELECT
    old_users.имэйл AS хуучин_систем,
    new_users.имэйл AS шинэ_систем
FROM old_users
FULL OUTER JOIN new_users ON old_users.имэйл = new_users.имэйл
WHERE old_users.имэйл IS NULL    -- зөвхөн шинэ системд байгаа
   OR new_users.имэйл IS NULL;   -- зөвхөн хуучин системд байгаа

Хоёр хүснэгтэд аль нэгэнд нь байхгүй бичлэгийг нэгэн зэрэг олох:

sql
-- Хичээл дуусгаагүй хэрэглэгч БА хэрэглэгчгүй хичээлүүдийг нэгэн зэрэг
SELECT
    u.нэр      AS хэрэглэгч,
    l.гарчиг   AS хичээл
FROM users AS u
FULL OUTER JOIN lesson_progress AS lp ON lp.user_id = u.id
FULL OUTER JOIN lessons AS l ON l.id = lp.lesson_id
WHERE lp.id IS NULL
ORDER BY u.нэр NULLS LAST;

CROSS JOIN — бүх хослолыг үүсгэх

CROSS JOIN нь нөхцөлгүйгээр хоёр хүснэгтийн бүх мөрийн бүх боломжит хослолыг үүсгэдэг. Үүнийг Декартын үржвэр гэнэ. Гаралтын мөрийн тоо = хүснэгт1-ийн мөр × хүснэгт2-ийн мөр.

sql
SELECT
    sizes.хэмжээ,
    colors.өнгө
FROM sizes
CROSS JOIN colors;

sizes (3 мөр): S, M, L colors (3 мөр): улаан, цэнхэр, ногоон

код
 хэмжээ |  өнгө
--------+--------
 S      | улаан
 S      | цэнхэр
 S      | ногоон
 M      | улаан
 M      | цэнхэр
 M      | ногоон
 L      | улаан
 L      | цэнхэр
 L      | ногоон
(9 rows)

CROSS JOIN-ны бодит хэрэглээ

sql
-- Бараа тус бүр, хэмжээ тус бүрийн агуулахын бүртгэл үүсгэх
CREATE TABLE sizes  (id SERIAL PRIMARY KEY, нэр TEXT);
CREATE TABLE products (id SERIAL PRIMARY KEY, нэр TEXT);

INSERT INTO sizes    VALUES (1,'S'),(2,'M'),(3,'L'),(4,'XL');
INSERT INTO products VALUES (1,'Цамц'),(2,'Өмд'),(3,'Хүрэм');

-- Бүх боломжит хослол
SELECT
    p.нэр    AS бараа,
    s.нэр    AS хэмжээ,
    0        AS нөөц
FROM products AS p
CROSS JOIN sizes AS s
ORDER BY p.нэр, s.id;
код
  бараа  | хэмжээ | нөөц
---------+--------+------
 Хүрэм   | S      |    0
 Хүрэм   | M      |    0
 Хүрэм   | L      |    0
 Хүрэм   | XL     |    0
 Өмд     | S      |    0
 ...

Тест өгөгдөл үүсгэхэд ч CROSS JOIN маш хэрэгтэй:

sql
-- generate_series ба CROSS JOIN-г хослуулан тест өгөгдөл үүсгэх
INSERT INTO inventory (product_id, size_id, нөөц)
SELECT p.id, s.id, FLOOR(RANDOM() * 100)::INTEGER
FROM products AS p
CROSS JOIN sizes AS s;

JOIN-уудын харьцуулалт

sql
-- Дараах өгөгдлөөр бүх join-г харьцуулъя:
-- А хүснэгт: 1, 2, 3
-- В хүснэгт: 2, 3, 4

-- INNER JOIN  → 2, 3          (хоёуланд нь байгаа)
-- LEFT JOIN   → 1, 2, 3       (А-гийн бүгд + В-ийн таарах)
-- RIGHT JOIN  → 2, 3, 4       (В-ийн бүгд + А-ийн таарах)
-- FULL OUTER  → 1, 2, 3, 4    (хоёуланд нь байгаагийн бүгд)
-- CROSS JOIN  → 3×3 = 9 мөр  (бүх хослол)

SELECT a.утга, b.утга
FROM table_a AS a
FULL OUTER JOIN table_b AS b ON a.id = b.id;

Анхааруулга: том хүснэгтэд CROSS JOIN болгоомжтой

1000 мөртэй хоёр хүснэгтийг CROSS JOIN хийвэл 1,000,000 мөр гарна. Санамсаргүй CROSS JOIN хийх нь систем гацах шалтгаан болж болно — ON нөхцөлөө мартсан эсэхийг шалга.

sql
-- Аюултай: WHERE эсвэл ON мартсан JOIN нь санамсаргүй CROSS JOIN болдог
SELECT * FROM users, orders;                      -- ❌ нөхцөлгүй, аюултай
SELECT * FROM users JOIN orders ON users.id = orders.user_id;  -- ✅ зөв

Дараагийн хичээлд:

COUNT, SUM, AVG, MIN, MAX зэрэг нэгтгэх функцүүдийг сурч — олон бичлэгийг нэг тоон хариу болгон боловсруулж сурна.