One-to-One munosabat — qachon kerak

~15 daq · MySQL · JOIN va Munosabatlar

Muammodan boshlaylik

foydalanuvchilar jadvali o'sib ketdi:

id | email | parol_hash | ism | avatar | bio | tugilgan_kun | manzil | telegram |
   | instagram | sayt | ish_joyi | lavozim | til | mavzu | xabar_sozlamalari | ...

40 ta ustun. Login paytida faqat email va parol_hash kerak, lekin har bir qator diskda katta joy egallaydi va MySQL ko'proq o'qiydi. Ko'pchilik foydalanuvchi profilni to'ldirmagan — 30 ta ustun NULL. Va har bir ustun qo'shilishi bilan jadval yanada og'irlashadi.

Bitta foydalanuvchiga — bitta profil. Bu ma'lumotni alohida jadvalga ajratsa bo'ladimi? Ha — bu One-to-One munosabat.


One-to-One nima?

Bir jadvaldagi bitta qatorga ikkinchi jadvalda ko'pi bilan bitta qator mos keladi.

CREATE TABLE foydalanuvchilar (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    email       VARCHAR(255) NOT NULL UNIQUE,
    parol_hash  VARCHAR(255) NOT NULL,
    yaratilgan  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE profillar (
    foydalanuvchi_id INT UNSIGNED PRIMARY KEY,     -- ← ham PK, ham FK
    ism              VARCHAR(100),
    avatar           VARCHAR(255),
    bio              TEXT,
    tugilgan_kun     DATE,
    telegram         VARCHAR(50),
    FOREIGN KEY (foydalanuvchi_id) REFERENCES foydalanuvchilar(id) ON DELETE CASCADE
);

Asosiy hiyla: foydalanuvchi_id — ham primary key, ham foreign key. Primary key takrorlanmaydi — demak bitta foydalanuvchiga ikkinchi profil yaratib bo'lmaydi. One-to-One ni baza o'zi kafolatlaydi.

Muqobil usul — alohida id + UNIQUE foreign key:

CREATE TABLE profillar (
    id               INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    foydalanuvchi_id INT UNSIGNED NOT NULL UNIQUE,   -- UNIQUE — bitta foydalanuvchiga bitta
    ...
);

Ikkalasi ham to'g'ri. Birinchisi ixchamroq.

[!INFO] UNIQUE siz bu oddiy One-to-Many bo'lib qoladi — bitta foydalanuvchiga ko'p profil. One-to-One va One-to-Many o'rtasidagi yagona farq — shu UNIQUE (yoki PRIMARY KEY).


So'rovlar

-- Login — faqat kichik jadval
SELECT id, parol_hash FROM foydalanuvchilar WHERE email = 'ali@mail.uz';

-- Profil sahifasi — ikkalasi birga
SELECT f.email, p.ism, p.avatar, p.bio
FROM foydalanuvchilar f
LEFT JOIN profillar p ON p.foydalanuvchi_id = f.id
WHERE f.id = 42;

LEFT JOIN — profilini hali to'ldirmagan foydalanuvchi ham ko'rinsin.

Profil yaratish yoki yangilash — upsert (9-dars):

INSERT INTO profillar (foydalanuvchi_id, ism, bio)
VALUES (42, 'Ali', 'PHP dasturchi')
AS yangi
ON DUPLICATE KEY UPDATE ism = yangi.ism, bio = yangi.bio;

Qachon One-to-One kerak?

One-to-One kam ishlatiladi. Ko'pincha shunchaki bitta jadval to'g'ri tanlov. Ajratishga faqat aniq sabab bo'lsa:

1. Ixtiyoriy ma'lumot

Faqat ba'zi qatorlarda bo'ladigan ko'p ustunlar:

-- Faqat yuridik shaxs mijozlar uchun
CREATE TABLE mijoz_kompaniyalari (
    mijoz_id     INT UNSIGNED PRIMARY KEY,
    kompaniya    VARCHAR(200) NOT NULL,
    inn          CHAR(9) NOT NULL UNIQUE,
    bank_hisobi  VARCHAR(30),
    direktor     VARCHAR(100),
    FOREIGN KEY (mijoz_id) REFERENCES mijozlar(id) ON DELETE CASCADE
);

Mijozlarning 95% i jismoniy shaxs. Bu 5 ta ustunni asosiy jadvalda saqlasak — 95% qatorda bo'sh. Alohida jadvalda esa ular NOT NULL bo'la oladi: kompaniya yozuvi bor bo'lsa — INN albatta bor.

2. Katta, kam o'qiladigan ma'lumot

CREATE TABLE maqola_matnlari (
    maqola_id  INT UNSIGNED PRIMARY KEY,
    matn       MEDIUMTEXT NOT NULL,
    FOREIGN KEY (maqola_id) REFERENCES maqolalar(id) ON DELETE CASCADE
);

Maqolalar ro'yxatida faqat sarlavha va sana kerak — 100 KB lik matnni har safar o'qish shart emas. Asosiy jadval kichik va tez qoladi.

3. Xavfsizlik va huquqlar

CREATE TABLE foydalanuvchi_maxfiy (
    foydalanuvchi_id  INT UNSIGNED PRIMARY KEY,
    pasport_seriya    VARCHAR(20),
    pinfl             CHAR(14),
    FOREIGN KEY (foydalanuvchi_id) REFERENCES foydalanuvchilar(id)
);

Maxfiy ma'lumotni alohida jadvalga qo'yib, unga faqat ma'lum MySQL foydalanuvchilariga ruxsat berish mumkin (50-dars). Analitika jamoasi asosiy jadvalni ko'radi, pasport ma'lumotlarini — yo'q.

4. Turli jamoalar / modullar

foydalanuvchilar — autentifikatsiya moduli, mijoz_sodiqlik (ballar, daraja) — marketing moduli. Har bir modul o'z jadvaliga egalik qiladi.


Qachon kerak emas

  • Ustunlar ko'p, lekin hammasi deyarli har doim to'ldiriladi va birga o'qiladi → bitta jadval
  • "Tartibli ko'rinsin" deb ajratish → keraksiz JOIN, murakkabroq kod
  • MySQL bir qatorda 4000 dan ortiq ustunni ko'taradi — 40 ta ustun o'z-o'zidan muammo emas

Qoida: default — bitta jadval. One-to-One — yuqoridagi sabablardan biri haqiqatan bo'lganda.


Uchta munosabat — xulosa

Munosabat Misol Qanday yasaladi
One-to-One Foydalanuvchi ↔ profil FK bola jadvalda + UNIQUE (yoki FK = PK)
One-to-Many Mijoz → buyurtmalar FK "ko'p" tomonda
Many-to-Many Mahsulot ↔ teglar Oraliq jadval, ikki FK, composite PK

Munosabatni aniqlash uchun ikki savol bering:

  1. "Bitta A nechta B ga ega bo'lishi mumkin?"
  2. "Bitta B nechta A ga ega bo'lishi mumkin?"
1-javob 2-javob Munosabat
Bitta Bitta One-to-One
Ko'p Bitta One-to-Many
Ko'p Ko'p Many-to-Many

Nima uchun shunday?

Nima uchun One-to-One da bola jadvalning primary key ini otaning id si qilish tavsiya etiladi?

Ikki sabab. Birinchidan, kafolat: primary key bo'lgani uchun takrorlanmaydi — kodda xato bo'lsa ham bitta foydalanuvchiga ikkita profil yaratib bo'lmaydi. Ikkinchidan, samaradorlik: InnoDB jadvallarni primary key bo'yicha tartiblab saqlaydi (clustered index, 30-dars). Profilni foydalanuvchi_id bo'yicha qidirish — to'g'ridan-to'g'ri primary key qidiruvi, eng tez yo'l. Alohida id va alohida UNIQUE indeks esa ikki qadamli qidiruv bo'lardi.


Xulosa

Eslab qoling:

  • One-to-One — bitta qatorga ko'pi bilan bitta qator
  • Yasash: bola jadvalda ota_id ham PRIMARY KEY, ham FOREIGN KEY
  • UNIQUE / PK siz bu One-to-Many bo'lib qoladi
  • Qachon: ixtiyoriy ustunlar guruhi, katta kam o'qiladigan ma'lumot, maxfiy ma'lumot
  • Default — bitta jadval; aniq sababsiz ajratmang
  • O'qishda — LEFT JOIN, yozishda — upsert

Keyingi dars

21-dars: Subquery — WHERE, FROM, SELECT ichida