Гадаад түлхүүр (FOREIGN KEY)
Бодит системд мэдээлэл нэг хүснэгтэд бүгд багтдаггүй — хэрэглэгч, захиалга, бараа гэх мэт олон хүснэгт хоорондоо холбоотой байдаг. FOREIGN KEY бол яг энэ холбоосыг баталгаажуулдаг механизм юм. "Байхгүй хэрэглэгчийн захиалга" эсвэл "байхгүй барааны жагсаалт" гэх мэт утгагүй мэдээлэл орохоос сэргийлдэг.
FOREIGN KEY гэж юу вэ?
Foreign key нь нэг хүснэгтийн баганыг өөр хүснэгтийн PRIMARY KEY-тэй холбодог хязгаарлалт юм. Жишээ нь:
-- Эх хүснэгт (parent table)
CREATE TABLE authors (
id BIGSERIAL PRIMARY KEY,
нэр TEXT NOT NULL
);
-- Хүүхэд хүснэгт (child table)
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
гарчиг TEXT NOT NULL,
author_id BIGINT REFERENCES authors(id) -- foreign key
);
REFERENCES authors(id) гэдэг нь author_id-д оруулах утга authors хүснэгтийн id-д заавал байх ёстой гэсэн утга. Байхгүй author_id оруулахыг оролдвол алдаа гарна:
INSERT INTO posts (гарчиг, author_id) VALUES ('Миний нийтлэл', 999);
-- ERROR: insert or update on table "posts" violates foreign key constraint
-- DETAIL: Key (author_id)=(999) is not present in table "authors".
REFERENCES бичих хэлбэрүүд
Inline хэлбэр (нэг баганад):
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
author_id BIGINT NOT NULL REFERENCES authors(id)
);
CONSTRAINT нэртэй хэлбэр (илүү тодорхой):
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
author_id BIGINT NOT NULL,
CONSTRAINT posts_author_id_fkey
FOREIGN KEY (author_id) REFERENCES authors(id)
);
Нийлмэл foreign key (хоёр баганаар холбох):
CREATE TABLE order_items (
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
тоо INTEGER NOT NULL,
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
ON DELETE — устгахад юу хийх вэ?
Эх бичлэг устгагдахад хүүхэд бичлэгт юу болохыг тодорхойлно:
| Сонголт | Тайлбар |
|---------|---------|
| RESTRICT | Хүүхэд бичлэг байвал устгахыг хориглоно (өгөгдмөл) |
| CASCADE | Хүүхэд бичлэгүүдийг хамт устгана |
| SET NULL | Хүүхэд баганыг NULL болгоно |
| SET DEFAULT | Хүүхэд баганыг DEFAULT утга болгоно |
| NO ACTION | RESTRICT-тэй ижил, шалгалтыг хойшлуулж болно |
-- Зохиогч устгагдахад нийтлэлүүд нь хамт устна
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
гарчиг TEXT NOT NULL,
author_id BIGINT REFERENCES authors(id) ON DELETE CASCADE
);
-- Хэрэглэгч устгагдахад захиалгын user_id нь NULL болно
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) ON DELETE SET NULL
);
-- Эх категори устгахыг хориглоно (доор нь бараа байвал)
CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
category_id INTEGER REFERENCES categories(id) ON DELETE RESTRICT
);
ON UPDATE — шинэчлэхэд юу хийх вэ?
Эх бичлэгийн PRIMARY KEY өөрчлөгдөхөд ховор ашиглагдана. CASCADE хамгийн нийтлэг:
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
author_id BIGINT REFERENCES authors(id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
Бодит жишээ: e-commerce схем
-- Хэрэглэгчид
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
нэр TEXT NOT NULL,
имэйл TEXT UNIQUE NOT NULL
);
-- Барааны ангилал
CREATE TABLE categories (
id BIGSERIAL PRIMARY KEY,
нэр TEXT UNIQUE NOT NULL
);
-- Бараа
CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
нэр TEXT NOT NULL,
үнэ NUMERIC(10,2) NOT NULL CHECK (үнэ > 0),
category_id BIGINT NOT NULL REFERENCES categories(id) ON DELETE RESTRICT
);
-- Захиалга
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
нийт NUMERIC(12,2) NOT NULL,
үүсгэсэн TIMESTAMPTZ DEFAULT NOW()
);
-- Захиалгын мөрүүд
CREATE TABLE order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE RESTRICT,
тоо INTEGER NOT NULL CHECK (тоо > 0),
үнэ NUMERIC(10,2) NOT NULL
);
Foreign key шалгах
Хүснэгтийн бүх foreign key-г харах:
SELECT
tc.constraint_name,
tc.table_name,
kcu.column_name,
ccu.table_name AS referenced_table,
ccu.column_name AS referenced_column
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_name = 'posts';
Дараагийн хичээлд:
Харилцаат хүснэгтүүдийг INNER JOIN-аар нэгтгэж, нэг query-д олон хүснэгтийн мэдээллийг хамтад нь уншиж сурна — SQL-ийн хамгийн хүчирхэг хэрэгслийг нэгдүгээрт судална.