6-bo‘lim
Birinchi normal shakl (1NF)
Atomar qiymat nima, ko'p qiymatli va takrorlanuvchi ustunlar, 1NF ga keltirish va JSON ustunlar haqida.
Ushbu bo‘lim mundarijasi
Normalizatsiya - takrorlanishni bosqichma-bosqich yo'q qilish usuli. Birinchi bosqich eng oddiy, lekin eng ko'p buziladigan.
1NF talabi #
Birinchi buzilish: ko'p qiymatli ustun #
CREATE TABLE mijozlar_yomon (
id INT PRIMARY KEY,
ism VARCHAR(100),
telefonlar VARCHAR(255),
manzil VARCHAR(255)
);
INSERT INTO mijozlar_yomon VALUES
(1, 'Husanboy Qodirov', '901234567,935556677', 'Namangan, Navoiy 12'),
(2, 'Malika Yusupova', '911112233', 'Farg''ona, Mustaqillik 5'),
(3, 'Kamola Rasulova', '', 'Andijon, Bobur 8');
-- "935556677 raqami kimga tegishli?" - qanday topamiz?
SELECT ism, telefonlar
FROM mijozlar_yomon
WHERE telefonlar LIKE '%935556677%';
+------------------+---------------------+
| ism | telefonlar |
+------------------+---------------------+
| Husanboy Qodirov | 901234567,935556677 |
+------------------+---------------------+
LIKE '%...%' indeksdan foydalana olmaydiYuqoridagi so'rov ishlaydi, lekin butun jadvalni o'qiydi.
Indeks so'zning boshidan qidiradi - xuddi lug'atdagi kabi.
LIKE 'abc%' indeksdan foydalanadi, LIKE '%abc%' esa yo'q.
Million qatorli jadvalda farq:
| So'rov | Vaqti |
|---|---|
WHERE raqam = '935556677' (indeksli) | ~1 ms |
WHERE telefonlar LIKE '%935556677%' | ~2000 ms |
Yana bir jiddiy muammo - noto'g'ri moslik:
WHERE telefonlar LIKE '%5556677%'
Bu 935556677 ni ham, 15556677 ni ham topadi. Chegara
belgilari yo'q.
Va nihoyat: '901234567,935556677' da bo'shliq bo'lsa
(', ') yoki bo'lmasa - qidiruv natijasi o'zgaradi. Ma'lumot
formati kafolatlanmagan.
1NF ga keltirish #
CREATE TABLE mijozlar (
id INT PRIMARY KEY AUTO_INCREMENT,
ism VARCHAR(100) NOT NULL,
shahar VARCHAR(50) NOT NULL,
kocha VARCHAR(100) NOT NULL,
uy VARCHAR(20) NOT NULL
);
CREATE TABLE telefonlar (
id INT PRIMARY KEY AUTO_INCREMENT,
mijoz_id INT NOT NULL,
raqam VARCHAR(20) NOT NULL,
turi ENUM('mobil', 'uy', 'ish') NOT NULL DEFAULT 'mobil',
UNIQUE (raqam),
FOREIGN KEY (mijoz_id) REFERENCES mijozlar(id) ON DELETE CASCADE
);
INSERT INTO mijozlar (ism, shahar, kocha, uy) VALUES
('Husanboy Qodirov', 'Namangan', 'Navoiy', '12'),
('Malika Yusupova', 'Farg''ona', 'Mustaqillik', '5'),
('Kamola Rasulova', 'Andijon', 'Bobur', '8');
INSERT INTO telefonlar (mijoz_id, raqam, turi) VALUES
(1, '901234567', 'mobil'),
(1, '935556677', 'ish'),
(2, '911112233', 'mobil');
Endi raqam bo'yicha aniq qidiruv mumkin:
SELECT m.ism, t.raqam, t.turi
FROM mijozlar m
JOIN telefonlar t ON t.mijoz_id = m.id
WHERE t.raqam = '935556677';
+------------------+-----------+------+
| ism | raqam | turi |
+------------------+-----------+------+
| Husanboy Qodirov | 935556677 | ish |
+------------------+-----------+------+
Endi qidiruv aniq tenglik bo'yicha ketmoqda va indeksdan foydalanadi.
Nima yutdik #
-- 1. Telefon turini bilamiz
SELECT m.ism, COUNT(t.id) AS raqamlar, GROUP_CONCAT(t.turi ORDER BY t.id) AS turlari
FROM mijozlar m
LEFT JOIN telefonlar t ON t.mijoz_id = m.id
GROUP BY m.id, m.ism
ORDER BY m.id;
+------------------+----------+-----------+
| ism | raqamlar | turlari |
+------------------+----------+-----------+
| Husanboy Qodirov | 2 | mobil,ish |
| Malika Yusupova | 1 | mobil |
| Kamola Rasulova | 0 | NULL |
+------------------+----------+-----------+
-- 2. Takroriy raqamni tizim rad etadi
INSERT INTO telefonlar (mijoz_id, raqam) VALUES (2, '901234567');
ERROR 1062 (23000): Duplicate entry '901234567' for key 'raqam'
-- 3. Shahar bo'yicha guruhlash oson
SELECT shahar, COUNT(*) AS mijozlar
FROM mijozlar
GROUP BY shahar
ORDER BY shahar;
+----------+----------+
| shahar | mijozlar |
+----------+----------+
| Andijon | 1 |
| Farg'ona | 1 |
| Namangan | 1 |
+----------+----------+
Bu muvozanat masalasi:
| Bo'lish darajasi | Qachon |
|---|---|
Bitta manzil matni | Faqat chop etish uchun kerak bo'lsa |
shahar + qolgani | Shahar bo'yicha filtr kerak bo'lsa |
shahar, kocha, uy | Yetkazib berish, statistika |
+ viloyat_id (jadval) | Rasmiy hisobot, standart ro'yxat |
Qoida: ma'lumot bo'yicha qidirasizmi yoki guruhlaysizmi? Agar ha - alohida ustun kerak.
Ortiqcha bo'lish ham zarar: hech kim uy raqamini alohida
so'ramasa, uni ajratish faqat murakkablik qo'shadi.
Xalqaro loyihalarda manzil formati mamlakatlarga qarab
juda farq qiladi - shuning uchun ko'pincha erkin matn
maydonlari va bitta mamlakat kodi ishlatiladi.
Ikkinchi buzilish: takrorlanuvchi ustunlar #
CREATE TABLE talabalar_yomon (
id INT PRIMARY KEY,
ism VARCHAR(100),
fan1 VARCHAR(50),
fan2 VARCHAR(50),
fan3 VARCHAR(50)
);
INSERT INTO talabalar_yomon VALUES
(1, 'Husanboy', 'Matematika', 'Fizika', 'Ingliz tili'),
(2, 'Malika', 'Matematika', NULL, NULL);
-- "Fizikaga yozilganlarni top" - uchala ustunni tekshirish kerak
SELECT ism FROM talabalar_yomon
WHERE fan1 = 'Fizika' OR fan2 = 'Fizika' OR fan3 = 'Fizika';
+----------+
| ism |
+----------+
| Husanboy |
+----------+
fan1, fan2, fan3 - bu aslida massiv, faqat ustunlarga
yoyilgan.
Muammolari:
| Muammo | Izoh |
|---|---|
| Chegara qattiq | To'rtinchi fan kerak bo'lsa - ALTER TABLE |
| Har so'rov uzun | OR zanjiri, har yangi ustun bilan o'sadi |
| Indeks samarasiz | Har ustunga alohida indeks kerak |
NULL lar ko'p | Bitta fan olgan talabada 2 ta bo'sh ustun |
| Tartib ma'nosiz | fan1 va fan2 orasida farq bormi? |
| Sanash qiyin | COUNT o'rniga qo'lda tekshiruv |
Xuddi shu naqsh boshqa ko'rinishlarda ham uchraydi:
telefon1, telefon2, telefon3
manzil_uy, manzil_ish
rasm_1 ... rasm_10
Har safar javob bir xil: alohida jadval.
Belgi: ustun nomida raqam bo'lsa - deyarli har doim 1NF buzilgan.
To'g'ri yechim #
CREATE TABLE fanlar (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE talabalar (
id INT PRIMARY KEY AUTO_INCREMENT,
ism VARCHAR(100) NOT NULL
);
CREATE TABLE yozilishlar (
talaba_id INT NOT NULL,
fan_id INT NOT NULL,
PRIMARY KEY (talaba_id, fan_id),
FOREIGN KEY (talaba_id) REFERENCES talabalar(id),
FOREIGN KEY (fan_id) REFERENCES fanlar(id)
);
INSERT INTO fanlar (nom) VALUES ('Matematika'), ('Fizika'), ('Ingliz tili');
INSERT INTO talabalar (ism) VALUES ('Husanboy'), ('Malika');
INSERT INTO yozilishlar VALUES (1, 1), (1, 2), (1, 3), (2, 1);
Endi so'rov qisqa va indeksli:
SELECT t.ism
FROM talabalar t
JOIN yozilishlar y ON y.talaba_id = t.id
JOIN fanlar f ON f.id = y.fan_id
WHERE f.nom = 'Fizika';
+----------+
| ism |
+----------+
| Husanboy |
+----------+
-- To'rtinchi fan qo'shish - ALTER TABLE kerak emas
INSERT INTO fanlar (nom) VALUES ('Tarix');
INSERT INTO yozilishlar VALUES (2, 4);
SELECT t.ism, COUNT(*) AS fanlar_soni
FROM talabalar t
JOIN yozilishlar y ON y.talaba_id = t.id
GROUP BY t.id, t.ism
ORDER BY t.id;
+----------+-------------+
| ism | fanlar_soni |
+----------+-------------+
| Husanboy | 3 |
| Malika | 2 |
+----------+-------------+
Atomarlik nisbiy tushuncha #
-- Ism atomarmi? Bu ehtiyojga bog'liq
CREATE TABLE variantlar (
id INT PRIMARY KEY AUTO_INCREMENT,
toliq_ism VARCHAR(200),
ism VARCHAR(100),
familiya VARCHAR(100),
otasining_ismi VARCHAR(100)
);
INSERT INTO variantlar (toliq_ism, ism, familiya, otasining_ismi) VALUES
('Qodirov Husanboy Baxtiyor o''g''li', 'Husanboy', 'Qodirov', 'Baxtiyor');
SELECT
toliq_ism,
CONCAT(familiya, ' ', LEFT(ism, 1), '.') AS qisqartma
FROM variantlar;
+----------------------------------+------------+
| toliq_ism | qisqartma |
+----------------------------------+------------+
| Qodirov Husanboy Baxtiyor o'g'li | Qodirov H. |
+----------------------------------+------------+
1NF "qiymat bo'linmas bo'lsin" deydi. Lekin bo'linmaslik sizning ehtiyojingizga bog'liq:
| Ma'lumot | Atomarmi? |
|---|---|
'Husanboy Qodirov' | Faqat chop etsangiz - ha |
'Husanboy Qodirov' | Familiya bo'yicha saralasangiz - yo'q |
'2026-09-10' | Odatda ha |
'2026-09-10' | Oy bo'yicha guruhlasangiz - DATE turi buni beradi |
'#FF5733' | Rang kodi - ha |
Amaliy savol: shu qiymatning bir qismi bo'yicha qidirasizmi yoki saralaysizmi?
- Ha → alohida ustun kerak;
- Yo'q → bitta ustun yetarli.
Vergulli ro'yxat esa har doim 1NF buzilishi, chunki uning elementlari mustaqil mavjudliklar.
JSON ustunlar - istisno bormi? #
CREATE TABLE mahsulotlar (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(100) NOT NULL,
narx DECIMAL(10,2) NOT NULL,
xossalar JSON
);
INSERT INTO mahsulotlar (nom, narx, xossalar) VALUES
('Noutbuk', 8000000, '{"ram": "16GB", "ekran": "15.6", "rang": "kulrang"}'),
('Telefon', 4000000, '{"ram": "8GB", "kamera": "48MP"}');
SELECT nom,
JSON_VALUE(xossalar, '$.ram') AS ram,
JSON_VALUE(xossalar, '$.kamera') AS kamera
FROM mahsulotlar
ORDER BY id;
+---------+------+--------+
| nom | ram | kamera |
+---------+------+--------+
| Noutbuk | 16GB | NULL |
| Telefon | 8GB | 48MP |
+---------+------+--------+
JSON ustun 1NF ni rasman buzadi, lekin ba'zan asosli:
JSON o'rinli bo'lgan holatlar:
| Holat | Nima uchun |
|---|---|
| Har mahsulotning turlicha xossalari | Ustun yaratib bo'lmaydi |
| Tashqi API javobini saqlash | Tuzilma o'zgarishi mumkin |
| Sozlamalar, jurnal ma'lumoti | Qidirilmaydi |
| Prototip bosqichi | Sxema hali aniq emas |
JSON dan qochish kerak bo'lgan holatlar:
| Holat | Nima uchun |
|---|---|
| Ma'lumot bo'yicha qidiriladi | Indeks murakkab, sekin |
| Tashqi kalit kerak | JSON ichida FK bo'lmaydi |
| Qiymat majburiy | NOT NULL ichkarida ishlamaydi |
| Hisobot tuziladi | JOIN va agregatsiya og'ir |
Nima yo'qotiladi:
- Tur tekshiruvi -
"ram": 16va"ram": "16GB"ikkalasi o'tadi; - Cheklovlar -
CHECK,UNIQUEishlamaydi; - Tashqi kalit - yo'q;
- Oddiy indeks - generatsiyalangan ustun kerak.
Qoida: avval oddiy ustun, JSON faqat haqiqatan dinamik ma'lumot uchun. "Keyin kerak bo'ladi" degan sabab bilan JSON qo'ymang.
- Vergulli telefon ro'yxatidan raqam qidiring.
LIKE '%...%'nima uchun indeksdan foydalanmasligini ayting.- Telefonlarni alohida jadvalga chiqaring.
- Telefon turini qo'shib, guruhlab chiqaring.
- Takroriy raqam kiritishga urinib ko'ring.
fan1,fan2,fan3ustunli jadvaldan qidiring.- Takrorlanuvchi ustunlarning oltita muammosini sanang.
- Bog'lovchi jadvalga o'tkazib, to'rtinchi fanni qo'shing.
- "Atomar" tushunchasi nima uchun nisbiy ekanini tushuntiring.
- JSON ustun o'rinli va o'rinsiz bo'lgan holatlarni ayting.
Xulosa #
- 1NF: har katakda aynan bitta qiymat.
- Vergulli ro'yxat - eng ko'p uchraydigan 1NF buzilishi.
LIKE '%...%'indeksdan foydalana olmaydi.- Raqamli ustun nomlari (
fan1,fan2) - yashirin massiv belgisi. - Takrorlanuvchi ustunlar qattiq chegara qo'yadi.
- "Atomar" kontekstga bog'liq - qidirasizmi yoki saralaysizmi?
- Manzilni ehtiyojga qarab bo'ling, ortiqcha emas.
- JSON 1NF ni buzadi - faqat haqiqatan dinamik ma'lumot uchun.
- JSON da tur tekshiruvi, cheklov va tashqi kalit yo'qoladi.
Keyingi bo'limda ikkinchi normal shaklni ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.