এই পর্বে যা শিখবেন
- BCNF-এর সংজ্ঞা, 3NF-এর সাথে ঠিক কোথায় পার্থক্য, আর কেন BCNF ⇒ 3NF কিন্তু উল্টোটা নয়
- BCNF decomposition অ্যালগরিদম — হাতে ধাপে ধাপে, projected FD-র ফাঁদসহ
- Lossless join-এর binary test (প্রমাণসহ) আর তিন বা বেশি টুকরোর জন্য chase পদ্ধতি
- Dependency preservation যাচাই, আর হারানো নিয়ম রক্ষার দাম (join বা trigger)
- MVD ও 4NF, join dependency ও 5NF, 6NF/DKNF-এর পরিচয়, আর পরীক্ষার ধাঁচের পূর্ণ সমাধান
১ · 3NF-এর পরেও যে ফাঁক থাকে — শিক্ষার্থী, কোর্স আর শিক্ষক
পর্ব ৩৫-এ দেখেছেন functional dependency (FD) X → Y মানে “X-এর মান জানলে Y-এর মান নির্ধারিত”, আর attribute closure X⁺ দিয়ে বের করা যায় X থেকে কী কী নির্ধারিত হয়। পর্ব ৩৬-এ 1NF, 2NF আর 3NF দিয়ে টেবিল ভেঙে partial আর transitive dependency সরিয়েছেন। মনে হতে পারে কাজ শেষ। কিন্তু একটি ছোট, বিখ্যাত টেবিল দেখায়, 3NF-এ থেকেও anomaly থাকতে পারে।
একটি বিশ্ববিদ্যালয়ের নিয়ম ভাবুন। একটি কোর্স (যেমন DBMS) একাধিক শিক্ষক পড়ান, প্রত্যেকে নিজের section. কিন্তু প্রত্যেক শিক্ষক ঠিক একটি কোর্স পড়ান। আর একজন শিক্ষার্থী একটি কোর্স একজন শিক্ষকের কাছেই নেন। রেজিস্টারে সব এক টেবিলে লেখা: sci(student, course, instructor). সংক্ষেপে S, C, I.
{S, C} → I — একজন শিক্ষার্থী আর একটি কোর্স জানলে শিক্ষক একজনই।
I → C — শিক্ষক জানলে কোর্স জানা যায়।
মুদি দোকানের খাতায় প্রতিটি বিক্রির লাইনে “পণ্য — সরবরাহকারী — সরবরাহকারীর এলাকা” লিখলে যা হয়, এখানেও তাই। Karim যে DBMS পড়ান, এই একটি তথ্য তাঁর প্রত্যেক শিক্ষার্থীর সারিতে আবার লেখা হয়। ৬০ জন শিক্ষার্থী মানে একই তথ্য ৬০ বার। যেকোনো একটি ভুল কপি মানেই খাতা নিজের সাথেই অমিল।
নিচের এডিটরের টেবিলে ৫টি সারি: Arif আর Chayon DBMS পড়েন Karim-এর কাছে, Bithi DBMS পড়েন Nasrin-এর কাছে, আর Arif ও Bithi Networks পড়েন Rafiq-এর কাছে। primary key (student, course). এবার দুটি সাধারণ ভুল ঘটান:
-- Karim এখন থেকে AI পড়াবেন; ভুল করে শুধু Arif-এর সারিটি বদলানো হলো
UPDATE sci SET course = 'AI' WHERE student = 'Arif' AND instructor = 'Karim';
SELECT instructor, GROUP_CONCAT(DISTINCT course) AS courses
FROM sci GROUP BY instructor HAVING COUNT(DISTINCT course) > 1;
-- Bithi DBMS ছেড়ে দিলেন: Nasrin যে DBMS পড়ান, সেই তথ্যও গেল
DELETE FROM sci WHERE student = 'Bithi' AND course = 'DBMS';
SELECT * FROM sci WHERE instructor = 'Nasrin';প্রথম SELECT-এ একটি সারি: Karim | AI,DBMS. একই শিক্ষক এখন দুটি কোর্সের নামে — I → C ভেঙে গেছে, অথচ DBMS কিছুই বলেনি। কারণ টেবিলের একমাত্র key (student, course) এই নিয়ম পাহারা দেয় না। এটি update anomaly: একটি তথ্যের অনেক কপি, একটি বদলালে বাকিগুলো পুরোনো। দ্বিতীয় SELECT-এ “(কোনো সারি নেই)”। Bithi-র সারি মুছতেই “Nasrin DBMS পড়ান” তথ্যটিও হারিয়ে গেল — deletion anomaly. উল্টো দিকে, নতুন শিক্ষক যোগ দিলে কোনো শিক্ষার্থী না পাওয়া পর্যন্ত তাঁর কোর্স লেখা যায় না, কারণ student key-র অংশ, NULL হতে পারে না — insertion anomaly.
অথচ এই টেবিল 3NF-এ আছে, অংশ ২-এ তা প্রমাণ করব। সমস্যার মূল হলো I → C: বাঁ দিকের I একটি superkey নয়, তাই একই (I, C) জোড়া বহু সারিতে ফিরে আসে। 3NF এই FD-কে ছাড় দেয়, কারণ C একটি candidate key-র অংশ। BCNF সেই ছাড় তুলে দেয়।
২ · BCNF — প্রতিটি নির্ধারক একটি superkey
BCNF (Boyce–Codd normal form): R একটি FD-সেট F-এর সাপেক্ষে BCNF-এ থাকবে, যদি F⁺-এর প্রতিটি FD X → Y-এর জন্য অন্তত একটি সত্য হয়: (ক) X → Y trivial (Y ⊆ X), অথবা (খ) X হলো R-এর একটি superkey.
3NF: একই শর্ত, তবে তৃতীয় একটি বিকল্প আছে: (গ) Y − X-এর প্রতিটি attribute prime, অর্থাৎ কোনো না কোনো candidate key-র অংশ।
দুই সংজ্ঞা পাশাপাশি রাখলেই একটি উপপাদ্য চোখে পড়ে। BCNF ⇒ 3NF: BCNF-এ প্রতিটি FD (ক) বা (খ) মানে। 3NF চায় (ক), (খ) বা (গ)। যে (ক) বা (খ) মানে, সে নিশ্চয়ই “(ক) বা (খ) বা (গ)” মানে। ∎ উল্টোটা সত্য নয়। পার্থক্য শুধু বিকল্প (গ)-তে: বাঁ দিক superkey নয়, কিন্তু ডান দিক prime — এমন FD 3NF মেনে নেয়, BCNF নয়।
পরীক্ষার কাজে একটি সুবিধা আছে: BCNF যাচাইয়ে F⁺-এর সব FD দেখতে হয় না। R-এর ওপর দেওয়া F-এর প্রতিটি FD X → Y দেখলেই চলে। কোনোটিতে X⁺ ≠ R হলে লঙ্ঘন, না হলে R BCNF-এ (প্রমাণ: F-এর সব বাঁ দিক superkey হলে, Armstrong-এর নিয়মে উৎপন্ন প্রতিটি non-trivial FD-র বাঁ দিকও superkey হয়)। তবে সাবধান, এই shortcut শুধু মূল R-এর জন্য। ভাঙার পর টুকরোর জন্য খাটে না, অংশ ৩-এ দেখবেন।
এবার শিক্ষার্থী–কোর্স–শিক্ষকের টেবিলে দুটি পরীক্ষা ধাপে ধাপে চালান:
| candidate key | |
| BCNF? | |
| 3NF? |
- টেবিল আর নিয়ম। তিনটি attribute, দুটি FD. প্রশ্ন দুটি: কোন কোন candidate key আছে, আর প্রতিটি FD-র বাঁ দিক superkey কি না।
- {S, C}-এর closure. SC → I দিয়ে I যোগ হয়, পুরো R পাওয়া গেল। S একা বা C একা কিছুই নির্ধারণ করে না, তাই {S, C} minimal — একটি candidate key.
- {S, I}-ও key. I → C দিয়ে C আসে, পুরো R. কিন্তু I⁺ = {I, C}, S⁺ = {S} — কোনো উপসেট একা যথেষ্ট নয়। তাই দুটি candidate key, আর S, C, I তিনটিই prime.
- BCNF পরীক্ষা। SC → I-এর বাঁ দিক key, ঠিক আছে। I → C-এর বাঁ দিক I, যার closure {I, C}. I superkey নয়, FD-টি non-trivial — BCNF ভাঙল।
- 3NF পরীক্ষা। 3NF-এর বিকল্প (গ): ডান দিকের C prime কি না। C আছে key {S, C}-তে, তাই হ্যাঁ। ফল: R 3NF-এ আছে, BCNF-এ নেই — অংশ ১-এর anomaly এই ফাঁকেরই ফল।
একটি দরকারি তথ্য মনে রাখুন: দুই attribute-এর যেকোনো relation BCNF-এ থাকে। R(A, B)-তে non-trivial FD হতে পারে শুধু A → B বা B → A. A → B থাকলে A⁺ = {A, B} = R, তাই A superkey. B → A-র বেলাতেও একই কথা। এজন্যই BCNF ভাঙনের শেষে প্রায়ই দুই-কলামের টেবিল দেখা যায়।
৩ · BCNF-এ ভাঙার অ্যালগরিদম
BCNF-এ নেই এমন টেবিলকে decomposition বা ভাঙনের মাধ্যমে ছোট টেবিলে আনা হয়। ভাঙন মানে R-এর attribute-গুলোকে কয়েকটি উপসেট R₁, …, Rₙ-এ ভাগ করা, যাদের মিলন R, আর প্রতিটি টুকরোর ডেটা হলো মূল ডেটার projection. অ্যালগরিদম সহজ: লঙ্ঘনকারী একটি FD খুঁজুন, তার বাঁ দিক ধরে দুই ভাগ করুন, প্রতিটি ভাগে আবার একই কাজ।
BCNF-DECOMPOSE(R, F):
result = { R }
while result-এ এমন Ri আছে, যা BCNF-এ নেই:
Ri-তে লঙ্ঘনকারী X → Y খুঁজুন // X ∩ Y = ∅, X⁺ ⊉ Ri
Y = (X⁺ ∩ Ri) − X // ডান দিক যতটা সম্ভব বড়
Ri-এর জায়গায় বসান:
(X ∪ Y) // X এখানে key
(Ri − Y) // X দুই টুকরোতেই থাকে
return resultপ্রতিবার টুকরো ছোট হয়, আর দুই কলামের টুকরো সবসময় BCNF-এ। তাই অ্যালগরিদম থামবেই। ডান দিক X⁺ পর্যন্ত বড় নেওয়ার কারণ: একসাথে যত বেশি attribute সরানো যায়, ভাঙনের ধাপ তত কম লাগে। এখন একটি বাস্তবধর্মী উদাহরণে চালাই। একটি ফলাফল-রেজিস্টার: R(roll, name, dept, head, course, grade), যেখানে head মানে বিভাগীয় প্রধান।
| টুকরোর সংখ্যা | |
| BCNF ভাঙছে |
- শুরু। ছয়টি attribute, তিনটি FD. roll আর course মিলে বাকি সব নির্ধারণ করে, আর কোনোটি একা পারে না — key {roll, course}.
- প্রথম লঙ্ঘন। roll → name, dept-এ roll⁺ = {roll, name, dept, head} (dept → head দিয়ে head-ও আসে)। এটি পুরো R নয়, তাই roll superkey নয় — BCNF ভাঙল।
- ভাঙন। Y = roll⁺ − roll = {name, dept, head}. R1 = roll ∪ Y, আর R2 = R − Y = (roll, course, grade). দুই টুকরোতেই roll আছে, আর roll হলো R1-এর key — অংশ ৪-এ দেখবেন, এটিই lossless হওয়ার শর্ত।
- R1-এ আবার লঙ্ঘন। R1-এর key roll. কিন্তু dept → head এখানেও খাটে, আর dept⁺ ∩ R1 = {dept, head} — dept superkey নয়। তাই R1 ভাঙে (dept, head) আর (roll, name, dept)-এ।
- শেষ। তিনটি টুকরোর প্রতিটিতে যে FD খাটে, তার বাঁ দিক সেই টুকরোর key. তিনটি FD-র প্রতিটি পুরোপুরি একটি টুকরোর ভেতরে, তাই এখানে কোনো নিয়ম হারায়নি। সব সময় এমন সৌভাগ্য হয় না — শিক্ষক-কোর্সের টেবিলে দেখবেন।
Projected FD-র ফাঁদ: টুকরো Ri যাচাই করতে শুধু দেওয়া F-এর যেসব FD পুরোপুরি Ri-এর ভেতরে, সেগুলো দেখলে চলে না। দেখতে হবে F⁺-এর projection — Ri-এর ভেতরের যেকোনো X-এর জন্য X⁺ ∩ Ri. উদাহরণ: R(A, B, C, D, E), F = {A → B, BC → D}. A → B ধরে ভাঙলে আসে (A, B) আর (A, C, D, E). দ্বিতীয় টুকরোয় F-এর কোনো FD পুরোপুরি নেই, তাই মনে হতে পারে এটি BCNF-এ। কিন্তু AC⁺ = {A, B, C, D}, অর্থাৎ AC → D এখানে খাটে, আর এই টুকরোর key ACE. লঙ্ঘন! এটিকে আবার ভাঙতে হয় (A, C, D) আর (A, C, E)-তে।
এবার শিক্ষার্থী–কোর্স–শিক্ষকে অ্যালগরিদম চালান। লঙ্ঘনকারী FD একটিই: I → C, যেখানে I⁺ = {I, C}. তাই R₁ = (I, C) আর R₂ = R − {C} = (S, I). SQL-এ এরা হলো teaches(instructor, course), যার key instructor, আর takes(student, instructor), যার key (student, instructor). দুটিই দুই-কলামের, তাই BCNF-এ। Karim-এর কোর্স এখন এক ঘরেই লেখা — update anomaly গেল। কিন্তু একটি নিয়ম কোথায় গেল, দেখুন:
-- BCNF-এ update anomaly নেই: Karim-এর কোর্স এক ঘরেই
UPDATE teaches SET course = 'AI' WHERE instructor = 'Karim';
UPDATE teaches SET course = 'DBMS' WHERE instructor = 'Karim';
-- Arif DBMS পড়ছেন Karim-এর কাছে; এবার Nasrin-এর কাছেও (একই কোর্স!)
INSERT INTO takes VALUES ('Arif', 'Nasrin');
-- দুই টেবিল জুড়ে দেখি: {student, course} → instructor কি টিকে আছে?
SELECT t.student, c.course, COUNT(*) AS instructors
FROM takes t JOIN teaches c ON c.instructor = t.instructor
GROUP BY t.student, c.course HAVING COUNT(*) > 1;দুটি UPDATE-এর প্রতিটিতে “১টি সারি বদলেছে” — একজন শিক্ষকের কোর্স বদলাতে একটিই সারি, অসংগতির সুযোগই নেই। কিন্তু INSERT-টিও নির্বিঘ্নে ঢুকল: (Arif, Nasrin) নতুন জোড়া, takes-এর key ভাঙেনি, আর Nasrin teaches-এ আছেন বলে foreign key-ও খুশি। শেষ SELECT-এর ফল: Arif | DBMS | 2. Arif এখন DBMS-এ দুজন শিক্ষকের কাছে — SC → I ভেঙেছে, অথচ কোনো টেবিল আপত্তি করেনি। কারণ S, C আর I কোনো একটি টেবিলে একসাথে নেই। এই নিয়মটি ভাঙনে হারিয়ে গেছে; অংশ ৫-এ এর আনুষ্ঠানিক নাম আর পরীক্ষা দেখবেন।
৪ · Lossless join — ভেঙে জোড়া দিলে ঠিক আগের টেবিল
যেকোনোভাবে ভাঙলেই চলবে না। ভাঙার পর দরকার হলে টুকরোগুলো natural join দিয়ে জুড়ে মূল তথ্য ফেরত পেতে হবে। ধরুন একটি বড় বই ছিঁড়ে দুই খণ্ড করলেন। পৃষ্ঠা নম্বর না থাকলে আবার সাজাতে গিয়ে ভুল ক্রমের অনেক “বই” তৈরি হবে। Join-এ সেই পৃষ্ঠা নম্বরের কাজ করে দুই টুকরোর সাধারণ attribute.
R-এর ভাঙন {R₁, R₂} lossless (lossless-join), যদি F মানে এমন R-এর প্রতিটি বৈধ instance r-এর জন্য πR1(r) ⋈ πR2(r) = r হয়। কোনো একটি বৈধ r-এ সমান না হলে ভাঙনটি lossy.
একটি সূক্ষ্ম কথা: r ⊆ πR1(r) ⋈ πR2(r) সবসময় সত্য। r-এর প্রতিটি সারি t-এর দুই টুকরো t[R₁] আর t[R₂] সাধারণ attribute-এ মেলে, তাই join-এ t আবার তৈরি হয়। অর্থাৎ join কখনো মূল সারি হারায় না। Lossy ভাঙন সারি হারায় না, বরং বাড়তি spurious tuple যোগ করে। “Loss” মানে হারায় তথ্য: কোন সারিগুলো সত্যি ছিল, তা আর আলাদা করা যায় না।
নিচের এডিটরে students(roll, name, dept) — ৪ জন: 101 Rahim CSE, 102 Karima EEE, 103 Rahim EEE, 104 Nusrat CSE. একজন ডিজাইনার ভাঙলেন (roll, dept) আর (name, dept)-এ। সাধারণ attribute শুধু dept.
-- খারাপ ভাঙন: (roll, dept) আর (name, dept) — মিল শুধু dept-এ
CREATE TABLE r1 AS SELECT roll, dept FROM students;
CREATE TABLE r2 AS SELECT DISTINCT name, dept FROM students;
SELECT COUNT(*) AS original FROM students;
SELECT r1.roll, r2.name, r1.dept
FROM r1 JOIN r2 ON r1.dept = r2.dept ORDER BY r1.roll, r2.name;original = 4, কিন্তু join-এ ৮টি সারি: 101 | Nusrat | CSE, 101 | Rahim | CSE, 102 | Karima | EEE, 102 | Rahim | EEE, 103 | Karima | EEE, 103 | Rahim | EEE, 104 | Nusrat | CSE, 104 | Rahim | CSE. চারটি আসল, চারটি ভুয়া। প্রতিটি CSE রোল প্রতিটি CSE নামের সাথে জুড়েছে, কারণ dept কোনো টুকরোর key নয় — একই dept বহু সারিতে, তাই মিল একের সাথে বহু। এখন “roll 101-এর নাম কী” প্রশ্নের দুটি উত্তর, কোনটি সত্য বলার উপায় নেই।
একই টেবিল এবার (roll, name) আর (roll, dept)-এ ভাঙুন। সাধারণ attribute roll, যা দুই টুকরোরই key:
-- ভালো ভাঙন: (roll, name) আর (roll, dept) — মিল roll-এ, যা key
CREATE TABLE r1 AS SELECT roll, name FROM students;
CREATE TABLE r2 AS SELECT roll, dept FROM students;
SELECT COUNT(*) AS joined FROM r1 JOIN r2 ON r1.roll = r2.roll;
-- join-এ এমন কোনো সারি কি আছে, যা মূল টেবিলে নেই?
SELECT r1.roll, r1.name, r2.dept FROM r1 JOIN r2 ON r1.roll = r2.roll
EXCEPT
SELECT roll, name, dept FROM students;joined = 4, আর EXCEPT-এর ফল “(কোনো সারি নেই)”। Join-এ মূল টেবিলের বাইরের কিছু নেই, আর সংখ্যাও সমান, তাই এই ডেটায় join ঠিক মূল টেবিল। তবে খেয়াল রাখুন: একটি নমুনায় ভুয়া সারি না আসা lossless-এর প্রমাণ নয়, শুধু খণ্ডন হয়নি। প্রমাণ আসে FD থেকে — নিচের পরীক্ষায়।
Binary test আর তার প্রমাণ
অর্থাৎ সাধারণ অংশটি অন্তত একটি টুকরোর superkey হতে হবে। ভালো ভাঙনে roll → (roll, name), তাই lossless. খারাপ ভাঙনে dept → roll নয়, dept → name-ও নয়, তাই lossy. এই শর্ত যে যথেষ্ট, তার প্রমাণ ছোট:
প্রমাণ (যথেষ্টতা): ধরুন X = R₁ ∩ R₂ আর X → R₁. Join-এর যেকোনো সারি t নিন। Join-এর সংজ্ঞায় r-এ একটি সারি t₁ আছে যার t₁[R₁] = t[R₁], আর একটি t₂ আছে যার t₂[R₂] = t[R₂]. দুটিই X-এ t-এর সাথে মেলে, তাই t₁[X] = t₂[X]. r মানে X → R₁, তাই t₁[R₁] = t₂[R₁]. তাহলে t₂[R₁] = t[R₁] আর t₂[R₂] = t[R₂], অর্থাৎ t₂ = t. তাই t ∈ r — join-এ নতুন কিছু নেই। আগেই দেখেছি r ⊆ join, তাই সমান। ∎
প্রয়োজনীয়তা (সংক্ষেপে): শুধু FD-ই নিয়ম হলে, দুই শর্তের কোনোটি না খাটলে দুই সারির একটি বৈধ instance বানানো যায়, যারা X-এ মেলে কিন্তু R₁ − X আর R₂ − X-এ আলাদা। তাদের join-এ চারটি সারি, যার দুটি ভুয়া। খারাপ ভাঙনের 101 আর 104 ঠিক এমন জোড়া। এই উপপাদ্যের আদি রূপকে Heath-এর উপপাদ্য (১৯৭১) বলা হয়: R(A, B, C)-তে A → B খাটলে (A, B) আর (A, C)-তে ভাঙন lossless.
এ থেকেই আসে একটি বড় ফল: BCNF decomposition সবসময় lossless. অ্যালগরিদমের প্রতিটি ধাপে টুকরো দুটি (X ∪ Y) আর (Ri − Y), তাদের ছেদ ঠিক X, আর X → X ∪ Y. অর্থাৎ binary test প্রতিবার পাস করে। Lossless ভাঙনের কোনো টুকরোকে আবার lossless-ভাবে ভাঙলে পুরোটাও lossless থাকে, তাই শেষ ফলও lossless. ∎
তিন বা বেশি টুকরো — chase (tableau) পদ্ধতি
টুকরো দুইয়ের বেশি হলে binary test সরাসরি খাটে না। তখন ব্যবহার হয় chase test, যা Aho, Beeri আর Ullman ১৯৭৯ সালে আনুষ্ঠানিক রূপ দেন। একটি টেবিল বানান, যাতে প্রতিটি টুকরোর জন্য একটি সারি। টুকরোর নিজের কলামে লিখুন “জানা” প্রতীক aⱼ, বাকি কলামে “অজানা” bᵢⱼ. তারপর FD প্রয়োগ করুন: দুটি সারি X-এ মিললে Y-তেও তাদের সমান করুন (একটি a থাকলে সবাই সেই a). কোনো সারি পুরোটা a হয়ে গেলে ভাঙন lossless; আর কিছু বদলানো না গেলে, অথচ সব-a সারি নেই, তবে lossy.
| প্রয়োগ করা FD | |
| সব-a সারি? |
- Tableau বানানো। প্রতিটি টুকরোর জন্য একটি সারি। R1-এর কলাম A, B, তাই সেখানে a1, a2; বাকি C, D-তে অজানা b13, b14. একইভাবে R2 আর R3. প্রশ্ন: FD প্রয়োগ করে কোনো সারিকে পুরো a বানানো যায় কি?
- A → B. A কলামে a1, b21, b31 — তিনটি আলাদা। কোনো দুটি সারি A-তে মেলে না, তাই এই FD কিছু বদলাতে পারে না।
- B → C. R1 আর R2 দুটিরই B = a2. FD বলে তাদের C-ও সমান হতে হবে। একটিতে a3, অন্যটিতে b13 — জানা মান জেতে, তাই R1-এর C হলো a3.
- C → D. এখন তিন সারিরই C = a3. তাই তিনটির D সমান হবে; R3-এ a4 আছে, তাই সবাই a4.
- সিদ্ধান্ত। R1-এর সারি এখন a1 a2 a3 a4 — পুরোটাই জানা মান। এর অর্থ: join-এর যেকোনো সারি মূল টেবিলের কোনো সারির সাথে মিলতে বাধ্য। তাই ভাঙনটি lossless. সব FD প্রয়োগের পরও সব-a সারি না এলে, শেষ tableau নিজেই একটি প্রতি-উদাহরণ — lossy.
৫ · Dependency preservation — নিয়মগুলো কি টুকরোয় টিকে থাকে
Lossless হলেই কাজ শেষ নয়। অংশ ৩-এর এডিটরে দেখেছেন, BCNF ভাঙনের পর Arif-কে DBMS-এ দুজন শিক্ষক দেওয়া গেল। ডেটা হারায়নি, কিন্তু একটি নিয়ম পাহারাহীন হয়েছে। ভালো ভাঙনের দ্বিতীয় গুণ তাই: প্রতিটি FD যেন কোনো একটি টুকরোর ভেতরে, শুধু সেই টুকরো দেখে যাচাই করা যায়। তাহলে প্রতিটি INSERT বা UPDATE-এ শুধু সেই টেবিলের key আর constraint দেখলেই চলে, join লাগে না।
Fᵢ = F⁺-এর সেই FD-গুলো, যাদের সব attribute Rᵢ-এর ভেতরে (F-এর projection). ভাঙনটি dependency preserving, যদি (F₁ ∪ F₂ ∪ … ∪ Fₙ)⁺ = F⁺ হয়।
F⁺ বের করা ব্যয়বহুল — attribute সংখ্যার সূচকীয় হারে বাড়তে পারে। তাই বাস্তবে F-এর প্রতিটি X → Y আলাদা করে যাচাই করা হয়, F⁺ না বানিয়েই:
PRESERVED(X → Y, {R1..Rn}, F):
result = X
repeat
for each Ri:
t = ((result ∩ Ri)⁺ ∩ Ri) // closure মূল F দিয়ে
result = result ∪ t
until result আর বদলায় না
return Y ⊆ resultযুক্তিটি এমন: result হলো “টুকরোগুলোর ভেতরের নিয়ম দিয়েই X থেকে যা যা পাওয়া যায়”। প্রতিটি টুকরোয় আমরা শুধু সেই টুকরোর attribute-গুলো দিয়ে এগোই। শেষে Y পাওয়া গেলে FD-টি টুকরোগুলো মিলিয়েই রক্ষা পায়। শিক্ষক-কোর্সের ভাঙনে পরীক্ষাটি চালান:
| result | |
| সিদ্ধান্ত |
- পদ্ধতি। দুই টুকরো: R1 = (I, C), যা teaches, আর R2 = (S, I), যা takes. দুটি FD আলাদা করে যাচাই করব।
- I → C সহজ। result {I} থেকে শুরু। R1-এর ভেতরে I⁺ ∩ R1 = {I, C}, তাই C পাওয়া গেল। এটি আসলে R1-এর primary key, যা DBMS নিজেই পাহারা দেয়।
- SC → I, R1-এ। result {S, C}. R1-এর সাথে ছেদ শুধু {C}, আর C একা কিছুই নির্ধারণ করে না। কিছু যোগ হলো না।
- SC → I, R2-এ। R2-এর সাথে ছেদ {S}; S⁺ = {S}. আবারও কিছু নেই। পুরো একটি চক্রে result বদলায়নি, তাই অ্যালগরিদম থামে।
- সিদ্ধান্ত। result = {S, C}-তে I নেই — SC → I রক্ষিত নয়। এর মানে কোনো একক টেবিলের constraint দিয়ে নিয়মটি ঠেকানো যায় না। এডিটরে ঠিক এটিই দেখেছেন।
বড় উপপাদ্য — BCNF বনাম 3NF
| গুণ | BCNF decomposition | 3NF synthesis |
|---|---|---|
| Lossless join | সবসময় | সবসময় (key-টেবিল যোগ করলে) |
| Dependency preservation | সবসময় নয় | সবসময় |
| FD-জনিত redundancy | নেই | কিছু থাকতে পারে (prime ছাড়ের জন্য) |
শিক্ষক-কোর্সের উদাহরণে এই দ্বন্দ্ব এড়ানোর উপায় নেই, প্রমাণও ছোট। R থেকে ছোট কোনো টুকরো, অর্থাৎ দুই attribute-এর টুকরো, নিলে সেখানে non-trivial FD পাওয়া যায় কেবল I → C — {S, C}-তে কেউ কাউকে নির্ধারণ করে না, {S, I}-তেও না। তাই যেকোনো ভাঙনের টুকরোগুলো মিলে শুধু I → C জানে, যা দিয়ে SC → I আসে না। SC → I রাখতে হলে একটি টুকরোকে পুরো (S, C, I) হতে হবে — সেটি তো BCNF-এ নেই। ∎ তাই এখানে বেছে নিতে হয়: BCNF (redundancy নেই, একটি নিয়ম পাহারাহীন) অথবা 3NF (নিয়ম পাহারায়, কিছু redundancy).
3NF synthesis অ্যালগরিদম উল্টো দিক থেকে আসে। F-এর canonical cover (পর্ব ৩৫) নিন; প্রতিটি X → Y থেকে একটি টেবিল (X ∪ Y) বানান; কোনো টেবিলে candidate key না থাকলে একটি key-টেবিল যোগ করুন; অন্য টেবিলের ভেতরে পুরোপুরি পড়ে যাওয়া টেবিল বাদ দিন। এখানে cover {SC → I, I → C} থেকে আসে (S, C, I) আর (I, C). দ্বিতীয়টি প্রথমটির ভেতরে পড়ে, তাই ফল মূল R-ই — 3NF-এ, সব নিয়ম রক্ষিত।
হারানো নিয়মের দাম — trigger দিয়ে পাহারা
BCNF বেছে নিলে SC → I রক্ষার দায় নিতে হবে অ্যাপ বা ডেটাবেসের কোডকে। প্রতিটি নতুন takes-সারির আগে join করে দেখতে হবে, এই শিক্ষার্থীর এই কোর্সে আগেই কোনো শিক্ষক আছেন কি না। SQL standard-এর CREATE ASSERTION এমন বহু-টেবিলের নিয়মের জন্যই, কিন্তু প্রচলিত বড় DBMS-গুলো এটি বাস্তবায়ন করেনি। ভরসা তাই trigger (পর্ব ২৯):
CREATE TRIGGER one_instructor_per_course BEFORE INSERT ON takes
WHEN EXISTS (SELECT 1 FROM takes t
JOIN teaches a ON a.instructor = t.instructor
JOIN teaches b ON b.instructor = NEW.instructor
WHERE t.student = NEW.student AND a.course = b.course)
BEGIN
SELECT RAISE(ABORT, 'student already has an instructor for this course');
END;
-- বৈধ: Chayon Networks নিলেন Rafiq-এর কাছে
INSERT INTO takes VALUES ('Chayon', 'Rafiq');
-- অবৈধ: Arif-এর DBMS-এ আগেই Karim আছেন
INSERT INTO takes VALUES ('Arif', 'Nasrin');Trigger তৈরি হয় (✓), Chayon-এর INSERT-এ “১টি সারি বদলেছে”, আর শেষ INSERT ত্রুটি দিয়ে বাতিল: student already has an instructor for this course. নিয়মটি আবার পাহারায়, কিন্তু দামটি দেখুন। প্রতিটি INSERT-এ একটি তিন-টেবিলের join চলে। এটুকুই যথেষ্টও নয় — takes-এর UPDATE আর teaches-এর UPDATE-ও (Nasrin-এর কোর্স বদলালে) নিয়ম ভাঙতে পারে, তাদের জন্যও trigger লাগবে। আর একসাথে আসা দুটি transaction দুজনেই “এখনো কেউ নেই” দেখে ফেলতে পারে, যদি isolation যথেষ্ট কড়া না হয় (পর্ব ৪৭)। Key-ভিত্তিক constraint এসব ঝামেলা ছাড়াই পাহারা দেয়। এজন্যই অনেক ডিজাইনার এমন ক্ষেত্রে 3NF-এ থেমে যান।
একটি মাঝামাঝি কৌশলও আছে: teaches-এ UNIQUE (instructor, course) রাখুন, আর তিন-কলামের sci টেবিলটি রেখে দিন, যেখানে key (student, course) আর FOREIGN KEY (instructor, course) REFERENCES teaches (instructor, course). তাহলে SC → I রক্ষা করে sci-এর key, আর I → C রক্ষা করে foreign key — কোনো trigger ছাড়াই। দাম: course দুই জায়গায় লেখা, কিন্তু DBMS নিজে দুই কপি মিলিয়ে রাখে। এটি নিয়ন্ত্রিত redundancy, পর্ব ৩৮-এর denormalization-এর এক আত্মীয়।
আরও গভীরে: কাগজে-কলমে সহজ দেখালেও এই প্রশ্নগুলোর কিছু গণনার দিক থেকে কঠিন। একটি attribute prime কি না, তা নির্ধারণ করা NP-complete (Lucchesi ও Osborn, ১৯৭৮) — তাই 3NF যাচাইও সাধারণভাবে কঠিন, যদিও BCNF যাচাই (মূল R-এর জন্য) শুধু closure দিয়েই বহুপদী সময়ে হয়। আবার ভাঙনের কোনো টুকরো BCNF-এ আছে কি না, projected FD-সহ যাচাই করাও কঠিন সমস্যা হিসেবে পরিচিত। বাস্তবের ছোট schema-য় এসব বাধা হয় না, কিন্তু স্বয়ংক্রিয় ডিজাইন-টুলগুলো এজন্যই নানা heuristic ব্যবহার করে।
৬ · Multivalued dependency আর 4NF — দুটি স্বাধীন তালিকা
এবার এমন redundancy, যা কোনো FD দিয়ে ধরা যায় না। একটি কোচিং সেন্টার প্রত্যেক শিক্ষকের দুটি তালিকা রাখে: কোন কোন বিষয় পড়াতে পারেন, আর কোন কোন ভাষায় পড়াতে পারেন। দুটি তালিকা একে অপরের ওপর নির্ভর করে না — Karim যে বিষয়ই পড়ান, তিনি বাংলা আর ইংরেজি দুটিতেই পারেন। এক টেবিলে রাখলে teacher_skills(teacher, subject, language), যার key তিনটি কলাম মিলে। কোনো non-trivial FD নেই, তাই টেবিলটি BCNF-এ। তবু Karim-এর ৩টি বিষয় আর ২টি ভাষা মানে ৩ × ২ = ৬টি সারি — প্রতিটি সংমিশ্রণ আলাদা করে লিখতে হয়।
X →→ Y (X multidetermines Y) R-এ খাটে, যদি Z = R − X − Y নিয়ে: X-এ মেলে এমন যেকোনো দুটি সারি t₁, t₂-এর জন্য r-এ একটি সারি t₃ থাকে, যার t₃[X] = t₁[X], t₃[Y] = t₁[Y] আর t₃[Z] = t₂[Z]. অর্থাৎ একই X-এর জন্য Y-এর মানগুলো আর Z-এর মানগুলো সব রকমভাবে জোড়া লাগে — Y-এর তালিকা Z থেকে স্বাধীন।
এখানে teacher →→ subject আর teacher →→ language. কয়েকটি মৌলিক তথ্য: (১) প্রতিটি FD একটি MVD — X → Y হলে X →→ Y. (২) পরিপূরক নিয়ম: X →→ Y হলে X →→ R − X − Y, তাই MVD সাধারণত জোড়ায় লেখা হয়: teacher →→ subject | language. (৩) X →→ Y trivial, যদি Y ⊆ X বা X ∪ Y = R.
-- Karim এখন হিন্দিতেও পড়াতে পারেন — কিন্তু ঢোকানো হলো একটিই সারি
INSERT INTO teacher_skills VALUES ('Karim', 'DBMS', 'Hindi');
-- MVD টিকে থাকলে সারি = (বিষয়ের সংখ্যা) × (ভাষার সংখ্যা)
SELECT teacher, COUNT(*) AS rows_now,
COUNT(DISTINCT subject) * COUNT(DISTINCT language) AS rows_needed
FROM teacher_skills GROUP BY teacher;ফল: Karim | 7 | 9 আর Nasrin | 3 | 3. শুরুতে Karim-এর ৬টি সারি ছিল (৩ বিষয় × ২ ভাষা)। একটি নতুন ভাষা মানে ৩টি নতুন সারি দরকার, কিন্তু ঢুকেছে ১টি। টেবিল এখন “বলছে” Karim DBMS হিন্দিতে পড়াতে পারেন, কিন্তু Networks বা AI পারেন না — যা মিথ্যা। কোনো key ভাঙেনি, তাই DBMS কিছু বলেনি। যেহেতু টেবিলে duplicate সারি নেই, প্রতিটি শিক্ষকের সারি-সংখ্যা আর বিষয় × ভাষার গুণফল সমান হওয়াই MVD টিকে থাকার শর্ত।
R 4NF-এ থাকবে, যদি প্রতিটি non-trivial MVD X →→ Y-তে X একটি superkey হয়। প্রতিটি FD একটি MVD, তাই 4NF ⇒ BCNF.
teacher_skills-এ teacher superkey নয়, তাই 4NF ভাঙে। সমাধান BCNF-এর মতোই: লঙ্ঘনকারী X →→ Y ধরে (X ∪ Y) আর (R − Y)-তে ভাঙা — এখানে (teacher, subject) আর (teacher, language). এই ভাঙন lossless, কারণ binary test-এর সাধারণ রূপ (Fagin, ১৯৭৭) বলে: {R₁, R₂} lossless ⇔ (R₁ ∩ R₂) →→ (R₁ − R₂). FD-র শর্তটি এরই বিশেষ রূপ।
-- 4NF: নতুন ভাষা মানে একটিই নতুন সারি
INSERT INTO teacher_language VALUES ('Karim', 'Hindi');
SELECT (SELECT COUNT(*) FROM teacher_subject)
+ (SELECT COUNT(*) FROM teacher_language) AS rows_stored;
-- join করলে পুরোনো টেবিলের সব সংমিশ্রণ ফিরে আসে
SELECT s.teacher, COUNT(*) AS combinations
FROM teacher_subject s JOIN teacher_language l ON l.teacher = s.teacher
GROUP BY s.teacher;rows_stored = 10 (৪টি বিষয়-সারি + ৬টি ভাষা-সারি), আর join-এ Karim | 9, Nasrin | 3. হিন্দি যোগ করতে একটিই INSERT লেগেছে, আর join আপনাআপনি Karim-এর ৩ × ৩ = ৯টি সংমিশ্রণ তৈরি করেছে — ভুল হওয়ার সুযোগই নেই। পুরোনো টেবিলে একই তথ্যে লাগত ৯ + ৩ = ১২টি সারি। শিক্ষক আর বিষয় যত বাড়বে, গুণফল বনাম যোগফলের এই ব্যবধান তত বাড়বে।
MVD আছে কি না, ঠিক করে নিয়ম, ডেটা নয়। যদি কোচিং সেন্টারের বাস্তবতা হয় “Karim DBMS শুধু ইংরেজিতে পড়ান, কিন্তু AI দুই ভাষাতেই”, তাহলে বিষয় আর ভাষা স্বাধীন নয়। তখন কোনো MVD নেই, তিন-কলামের টেবিলটিই সঠিক, আর ভাঙলে সেই তথ্য হারাবে — join ভুয়া সারি দেবে। ডিজাইনের আগে জিজ্ঞেস করুন: “এই দুটি তালিকা কি সত্যিই একে অপরের সাথে সম্পর্কহীন?”
৭ · Join dependency আর 5NF — সরবরাহকারী, যন্ত্রাংশ আর প্রকল্প
MVD হলো দুই টুকরোয় lossless ভাঙনের শর্ত। এর সাধারণ রূপ join dependency: *(R₁, R₂, …, Rₙ) খাটে, যদি প্রতিটি বৈধ r-এ r = πR1(r) ⋈ … ⋈ πRn(r) হয়। X →→ Y আসলে *(XY, XZ). কিছু নিয়ম আছে, যা দুই টুকরোয় ধরা যায় না, কিন্তু তিনটিতে যায়।
একটি নির্মাণ প্রতিষ্ঠানের spj(supplier, part, project): কোন সরবরাহকারী কোন প্রকল্পে কোন যন্ত্রাংশ দেয়। ধরুন ব্যবসার একটি চক্রাকার নিয়ম আছে: যদি S1 যন্ত্রাংশ P1 সরবরাহ করে (কোনো প্রকল্পে), আর P1 লাগে প্রকল্প J1-এ, আর S1 প্রকল্প J1-এ কিছু সরবরাহ করে — তাহলে S1 প্রকল্প J1-এ P1 সরবরাহ করে। নিয়মটি FD নয়, MVD-ও নয়। কিন্তু এর মানে হলো *(SP, PJ, SJ) — তিনটি জোড়া-টেবিল থেকে পুরো তথ্য ফেরত আসে।
এডিটরের টেবিলে ৪টি সারি: (S1, P1, J2), (S1, P2, J1), (S2, P1, J1), (S1, P1, J1). শেষ সারিটি নিয়মের ফল — প্রথম তিনটি থেকে এটি বাধ্যতামূলক। এবার তিনটি জোড়া-projection বানিয়ে জোড়া দিন:
CREATE TABLE sp AS SELECT DISTINCT supplier, part FROM spj;
CREATE TABLE pj AS SELECT DISTINCT part, project FROM spj;
CREATE TABLE sj AS SELECT DISTINCT supplier, project FROM spj;
-- দুটি অংশ জুড়লে: ভুয়া সারি আসে কি?
SELECT sp.supplier, sp.part, pj.project FROM sp JOIN pj ON pj.part = sp.part
EXCEPT SELECT * FROM spj;
-- তিনটি অংশ একসাথে জুড়লে কয়টি সারি?
SELECT COUNT(*) AS three_way FROM sp
JOIN pj ON pj.part = sp.part
JOIN sj ON sj.supplier = sp.supplier AND sj.project = pj.project;প্রথম SELECT-এ একটি ভুয়া সারি: S2 | P1 | J2. SP আর PJ জুড়লে S2–P1 আর P1–J2 মিলে এই সংমিশ্রণ তৈরি হয়, যা কখনো ঘটেনি। অন্য যেকোনো জোড়া জুড়লেও এমন ভুয়া সারি আসে — কোনো দুই-টুকরোর ভাঙনই lossless নয়। দ্বিতীয় SELECT-এ three_way = 4: তৃতীয় টুকরো SJ-তে (S2, J2) নেই, তাই ভুয়া সারিটি ছেঁটে যায়, আর ঠিক মূল চারটি সারি থাকে। নিয়মটি না থাকলে (শেষ সারি বাদ দিয়ে চালিয়ে দেখুন) তিন-টুকরোর join-এ (S1, P1, J1) ভুয়া হয়ে ঢুকত।
R 5NF-এ, বা project-join normal form-এ থাকবে, যদি R-এ খাটে এমন প্রতিটি non-trivial join dependency R-এর candidate key-গুলো থেকেই উৎপন্ন হয়। 5NF ⇒ 4NF.
spj-এর একমাত্র key তিনটি কলাম মিলে, আর *(SP, PJ, SJ) সেই key থেকে আসে না। তাই spj 4NF-এ (কোনো non-trivial MVD নেই) থেকেও 5NF-এ নেই। 5NF-এ আনতে তিনটি টেবিল sp, pj, sj রাখুন। চক্রাকার নিয়মটি তখন গঠনের ভেতরেই থাকে, আর “S1 এখন P3 দেয়” জাতীয় তথ্য একবারই লিখতে হয়। তবে মূল্য আছে: প্রশ্নের উত্তর পেতে তিন-টেবিলের join লাগে, আর নিয়মটি সত্যিই ব্যবসায় খাটে কি না, তা নিশ্চিত হতে হয়।
৮ · 6NF, DKNF আর normal form-এর সিঁড়ি — বাস্তবে কতদূর যাবেন
সিঁড়ির আরও দুটি ধাপ নাম হিসেবে জেনে রাখুন। 6NF: টেবিলে কোনো non-trivial join dependency-ই নেই — সাধারণত একটি key আর বড়জোর একটি অন্য কলাম। এর প্রধান ব্যবহার সময়ভিত্তিক ডেটায়: একজন কর্মচারীর বেতন, পদবি আর শাখা আলাদা আলাদা সময়ে বদলায়। এক টেবিলে (emp, salary, designation, from, to) রাখলে শুধু বেতন বদলালেও পুরো সারি নতুন করে লিখতে হয়। 6NF-এ প্রতিটি attribute নিজের টেবিলে, নিজের সময়-সীমা নিয়ে থাকে, যা temporal table-এর আলোচনায় আসে। DKNF (domain-key normal form): সব constraint শুধু domain constraint আর key constraint থেকেই আসে — একটি আদর্শ, যা সাধারণ নিয়মে পৌঁছানোর কোনো অ্যালগরিদম নেই।
Normal form-এর সিঁড়ি
Each inner box is stricter: every relation in an inner form is also in all outer forms
বাস্তব অবস্থান
- লক্ষ্য 3NF বা BCNF. এদের মধ্যে পার্থক্য আসে শুধু যখন candidate key একাধিক আর পরস্পর-ঢাকা (overlapping). বেশিরভাগ টেবিলে 3NF আর BCNF একই।
- BCNF ভাঙতে একটি নিয়ম হারালে দুটি পথ: 3NF-এ থেকে key দিয়ে পাহারা, বা BCNF-এ গিয়ে trigger/অ্যাপ-কোড দিয়ে পাহারা। কোন নিয়ম কতবার ভাঙার ঝুঁকি, তা দেখে বেছে নিন।
- 4NF-এর সমস্যা সাধারণত আসে দুটি আলাদা one-to-many তালিকা এক টেবিলে মেশালে — ER মডেল থেকে ঠিকমতো টেবিল বানালে (পর্ব ১০) এমনিতেই এড়ানো যায়।
- 5NF-এর সমস্যা বিরল, আসে শুধু ternary relationship-এ চক্রাকার ব্যবসায়িক নিয়ম থাকলে। কিন্তু ঘটলে নিঃশব্দে ভুল ডেটা তৈরি হয়।
- পড়ার গতির জন্য ইচ্ছে করে নিয়ম ভাঙা — denormalization — পরের পর্বের বিষয়। আগে normalize করে জানুন কী ভাঙছেন।
১৯৭০-৭১ সালে Edgar F. Codd 1NF, 2NF আর 3NF সংজ্ঞায়িত করেন। ১৯৭৪ সালে Codd ও IBM-এর তরুণ গবেষক Raymond F. Boyce একটি কড়া রূপ দেন, যা পরে তাঁদের দুজনের নামে BCNF নামে পরিচিত হয়। (কেউ কেউ লক্ষ করেন, Ian Heath ১৯৭১ সালেই প্রায় একই শর্ত লিখেছিলেন।) Boyce-ই Donald Chamberlin-এর সাথে SEQUEL ভাষার নকশা করেছিলেন, যা পরে SQL হয়; তিনি ১৯৭৪ সালেই অল্প বয়সে মারা যান। Ronald Fagin ১৯৭৭ সালে MVD আর 4NF, ১৯৭৯ সালে PJNF (5NF), আর ১৯৮১ সালে DKNF প্রকাশ করেন। 3NF synthesis-এর পদ্ধতি দেন Philip Bernstein (১৯৭৬)। 6NF-এর সংজ্ঞা আসে C. J. Date, Hugh Darwen আর Nikos Lorentzos-এর সময়ভিত্তিক ডেটার কাজ থেকে, ২০০০-এর দশকের শুরুতে।
৯ · পরীক্ষার ধাঁচের সমস্যা — পূর্ণ সমাধানসহ
বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর লিখিত পরীক্ষায় এই অধ্যায় থেকে সাধারণত একটি FD-সেট দিয়ে চার-পাঁচটি প্রশ্ন একসাথে আসে। নিজে আগে চেষ্টা করুন, তারপর সমাধান মেলান।
সমস্যা ১. R(A, B, C, D), F = {AB → C, C → A, C → D}. (ক) সব candidate key বের করুন। (খ) R সর্বোচ্চ কোন normal form-এ? (গ) BCNF-এ ভাঙুন; ভাঙনটি কি dependency preserving? (ঘ) 3NF synthesis করুন।
(ক) B কোনো FD-র ডান দিকে নেই, তাই প্রতিটি key-তে B থাকবেই। B⁺ = {B} — একা চলে না। AB⁺ = {A, B, C, D} (AB → C, তারপর C → D) — key. CB⁺ = {C, B, A, D} — key. DB⁺ = {D, B} — নয়। তাই candidate key: {A, B} আর {B, C}; prime attribute: A, B, C.
(খ) C → D-তে C superkey নয়, আর D prime নয় — 3NF ভাঙে। আরও দেখুন, C হলো key {B, C}-এর একটি প্রকৃত অংশ, আর D non-prime, তাই এটি partial dependency — 2NF-ও ভাঙে। সর্বোচ্চ: 1NF.
(গ) C → D ধরে: C⁺ = {C, A, D}. R1 = (A, C, D), R2 = R − {A, D} = (B, C). R1-এ C → A, C → D, key C — BCNF. R2 দুই কলামের — BCNF. ছেদ {C}, আর C → R1, তাই lossless. AB → C পরীক্ষা: result {A, B}; R1-এর সাথে ছেদ {A}, A⁺ = {A}; R2-এর সাথে ছেদ {B}, B⁺ = {B} — কিছু বাড়ে না। AB → C হারিয়েছে, ভাঙনটি dependency preserving নয়।
(ঘ) F-ই এখানে canonical cover (কোনো বাড়তি attribute বা FD নেই)। একই বাঁ দিক মিলিয়ে: AB → C, C → AD. টেবিল: (A, B, C) আর (A, C, D). প্রথমটিতে key {A, B} আছে, তাই আলাদা key-টেবিল লাগে না। (A, B, C) 3NF-এ (C → A-তে A prime), কিন্তু BCNF-এ নয় — অংশ ২-এর শিক্ষক-কোর্সের ঠিক একই নকশা।
সমস্যা ২. R(A, B, C), F = {A → B}. ভাঙন (ক) {(A, B), (B, C)} আর (খ) {(A, B), (A, C)} — কোনটি lossless? Lossy হলে একটি প্রতি-উদাহরণ দিন।
(ক) ছেদ {B}; B⁺ = {B} — B → AB নয়, B → BC-ও নয়। Lossy. প্রতি-উদাহরণ: r = {(1, x, 7), (2, x, 8)} — দুটিই A → B মানে। Projection: (A, B) = {(1, x), (2, x)}, (B, C) = {(x, 7), (x, 8)}. Join-এ ৪টি সারি: (1, x, 7), (1, x, 8), (2, x, 7), (2, x, 8) — দুটি ভুয়া। (খ) ছেদ {A}, আর A → B মানে A → (A, B) — binary test পাস, lossless (Heath-এর উপপাদ্য)।
সমস্যা ৩. R(course, teacher, book): প্রতিটি কোর্সের একদল শিক্ষক আর একটি নির্দিষ্ট বইয়ের তালিকা আছে; কোর্সের যেকোনো শিক্ষক সব বই ব্যবহার করেন। কোনো FD নেই। R কি BCNF-এ? 4NF-এ? প্রয়োজনে ভাঙুন।
কোনো non-trivial FD নেই, key তিনটি কলাম মিলে — তাই R BCNF-এ। কিন্তু course →→ teacher | book খাটে, কারণ শিক্ষক আর বইয়ের তালিকা পরস্পর-স্বাধীন। course superkey নয়, তাই 4NF ভাঙে। ভাঙন: (course, teacher) আর (course, book) — ছেদ course, আর course →→ teacher, তাই lossless. দুটি টেবিলেই key দুই কলাম মিলে, কোনো non-trivial MVD নেই — 4NF.
১০ · AI-এর যুগে normalization
AI-কে “এই টেবিলটি BCNF-এ নিয়ে যাও” বললে সে দ্রুত কয়েকটি টেবিল দেয়, প্রায়ই সঠিকও। কিন্তু এই অধ্যায়ের ভুলগুলো চোখে পড়ে না, কারণ ভাঙা টেবিলও চলে, কোয়েরিও চলে। AI সাধারণত যা ভুল করে:
| AI-এর ভুল | ফল | কীভাবে ধরবেন |
|---|---|---|
| FD নিজে থেকে অনুমান করে (কলামের নাম দেখে) | ভুল key, ভুল ভাঙন | প্রতিটি FD-র পেছনে ব্যবসার নিয়ম জিজ্ঞেস করুন |
| Non-key কলামে মিলিয়ে ভাঙে | lossy — join-এ ভুয়া সারি | প্রতিটি ভাঙনে ছেদ কোনো টুকরোর key কি না দেখুন; join-এর সারি গুনুন |
| “BCNF সবসময় ভালো” বলে হারানো FD উল্লেখ করে না | নিয়ম পাহারাহীন | dependency preservation পরীক্ষা চালান |
| টুকরো যাচাইয়ে শুধু দেওয়া FD দেখে | লুকোনো লঙ্ঘন থেকে যায় | প্রতিটি টুকরোয় X⁺ ∩ Ri হিসাব করুন |
| স্বাধীন নয় এমন তালিকাকে MVD ধরে 4NF-এ ভাঙে | তথ্য হারায় | ডেটায় সারি = গুণফল কি না পরীক্ষা করুন, আর নিয়মটি জিজ্ঞেস করুন |
| নতুন টেবিলে foreign key আর UNIQUE দেয় না | ভাঙন কাগজে ঠিক, ডেটায় অনাথ সারি | CREATE TABLE-গুলো constraint-সহ মিলিয়ে নিন |
AI-কে শুধু ফল না চেয়ে প্রতিটি ধাপের যুক্তি চান, যাতে আপনি প্রতিটি ধাপ নিজে মিলিয়ে নিতে পারেন:
AI-এর দেওয়া ভাঙন হাতে পেলে — যাচাইয়ের চেকলিস্ট
পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:
- 3NF আর BCNF-এর পার্থক্য? — 3NF-এ X → A মেনে নেওয়া হয় যদি A prime হয়; BCNF-এ X-কে সবসময় superkey হতে হয়।
- BCNF-এ আছে কিন্তু 3NF-এ নেই — এমন টেবিল সম্ভব? — না; BCNF ⇒ 3NF.
- Lossless join-এর শর্ত? — R₁ ∩ R₂ থেকে R₁ বা R₂ নির্ধারিত হতে হবে (সাধারণ অংশটি একটি টুকরোর superkey).
- BCNF ভাঙনের দুর্বলতা? — সবসময় lossless, কিন্তু dependency preserving না-ও হতে পারে; 3NF synthesis দুটিই দেয়।
- 4NF কোন সমস্যা সারায়? — একই key-র দুটি স্বাধীন multivalued তালিকা (MVD) এক টেবিলে থাকায় গুণফল-আকারের redundancy.
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে নিয়ম-মুখস্থ থেকে ডিজাইনের সিদ্ধান্তে নিয়ে যাবে।
প্র ০১BCNF যদি redundancy পুরোপুরি সরায়, তাহলে বহু পাঠ্যবই আর অভিজ্ঞ ডিজাইনার কেন বলেন “3NF-ই যথেষ্ট”?
কারণ দুটি গুণের মধ্যে একটি সত্যিকারের দ্বন্দ্ব আছে, আর ডিজাইনাররা জানেন কোনটি হারালে কত দাম। BCNF FD-জনিত redundancy সরায়, কিন্তু অংশ ৫-এ প্রমাণ দেখেছেন: শিক্ষার্থী–কোর্স–শিক্ষকের মতো নকশায় কোনো BCNF ভাঙনই SC → I রক্ষা করতে পারে না। 3NF synthesis সবসময় lossless আর dependency preserving — প্রতিটি নিয়ম কোনো একটি টেবিলের key দিয়ে পাহারা দেওয়া যায়।
দুই দামের তুলনা করুন। 3NF-এ থাকলে redundancy-র দাম সীমিত: শুধু “prime ছাড়” পাওয়া FD-গুলোর ডেটা একাধিকবার লেখা হয়, আর সেগুলো সাধারণত ছোট। BCNF-এ গেলে হারানো নিয়ম রক্ষার দাম কোডে যায়: INSERT ও UPDATE-এর trigger, প্রতিবার join, concurrency-র সূক্ষ্ম race condition. SQL standard এমন বহু-টেবিলের নিয়মের জন্য CREATE ASSERTION রেখেছিল (SQL-92), কিন্তু প্রচলিত বড় DBMS-গুলো এটি বাস্তবায়ন করেনি, কারণ যেকোনো বহু-টেবিলের শর্ত দক্ষভাবে আর concurrency-নিরাপদভাবে যাচাই করা কঠিন। তাই বাস্তবে “নিয়ম কোডে রাখা” মানে ঝুঁকি।
আরেকটি বাস্তব কথা: বেশিরভাগ টেবিলে 3NF আর BCNF একই। পার্থক্য আসে শুধু যখন একাধিক composite candidate key একে অপরকে ঢাকে। তাই “3NF-ই যথেষ্ট” কথাটির আসল অর্থ: সাধারণভাবে BCNF-এ যান, আর যেখানে BCNF একটি নিয়ম হারায়, সেখানে সচেতনভাবে 3NF-এ থামুন, কিংবা অংশ ৫-এর composite foreign key কৌশল দিয়ে দুটিই পান।
মূল উপলব্ধি: normal form কোনো প্রতিযোগিতার পদক নয়; এটি redundancy আর নিয়ম-পাহারার মধ্যে একটি দর-কষাকষি, আর সঠিক উত্তর নির্ভর করে কোন নিয়ম ভাঙলে ক্ষতি বেশি।
প্র ০২Lossy decomposition বাস্তব প্রজেক্টে কোথায় লুকিয়ে থাকে? Spreadsheet থেকে ডেটা আমদানি বা রিপোর্টিংয়ের join-এ এর চেহারা কেমন?
পাঠ্যবইয়ে lossy ভাঙন দেখায় “কেউ ভুল করে টেবিল ভাঙল”। বাস্তবে এটি প্রায়ই আসে উল্টো দিক থেকে: দুটি আলাদা উৎসের টেবিল এমন কলামে join করা হয়, যা কোনোটিরই key নয়। ধরুন একটি পোশাক কারখানার একটি শিটে আছে (line, buyer), আরেকটিতে (line, style). দুটিকে line ধরে জুড়লে প্রতিটি buyer প্রতিটি style-এর সাথে জুড়ে যায়, যদিও বাস্তবে একটি buyer-এর অর্ডার কয়েকটি style-এই। রিপোর্টে মোট পরিমাণ হঠাৎ কয়েক গুণ হয়ে যায় — পর্ব ১৯-এ যাকে fan-out বলা হয়েছে, এটি তারই তাত্ত্বিক নাম।
আরেকটি উৎস হলো “খরচ কমাতে” টেবিল ভাঙা। কেউ একটি বড় টেবিলকে দুটি ছোট টেবিলে ভাগ করে সাধারণ কলাম হিসেবে রাখেন একটি নাম বা তারিখ, id নয়। প্রথম দিনে ডেটা ছোট, ভুয়া সারি চোখে পড়ে না। ছয় মাস পরে একই নামের দুজন গ্রাহক এলে join দুজনকে মিলিয়ে ফেলে। Heath-এর উপপাদ্য (১৯৭১) এই বিপদের সরল প্রতিকার দেয়: ভাঙার সাধারণ অংশ অন্তত একটি টুকরোর key হতে হবে।
যাচাইয়ের অভ্যাস তাই সহজ আর যান্ত্রিক। যেকোনো ভাঙন বা join-এর পর তিনটি সংখ্যা দেখুন: মূল সারি, join-এর সারি, আর EXCEPT-এর ফল — যেমন অংশ ৪-এর এডিটরে। সংখ্যা বাড়লে বুঝবেন সাধারণ কলামটি key নয়। ডেটা warehouse-এর ETL-এ এই ধরনের row-count reconciliation একটি প্রচলিত নিয়মিত পরীক্ষা।
মূল উপলব্ধি: lossy ভাঙন সারি হারায় না, সারি বাড়ায়; তাই “কোনো ডেটা তো হারায়নি” দেখে নিশ্চিন্ত হবেন না — গুনে দেখুন বাড়তি কিছু এল কি না।
প্র ০৩ORM দিয়ে একজন গ্রাহকের সব অর্ডার আর সব ঠিকানা একসাথে আনতে গিয়ে অনেক অ্যাপ হঠাৎ ধীর হয়ে যায়। এর সাথে MVD আর 4NF-এর সম্পর্ক কী?
একজন গ্রাহকের অর্ডারের তালিকা আর ঠিকানার তালিকা দুটি স্বাধীন one-to-many সম্পর্ক — ঠিক teacher →→ subject | language-এর মতো। ডেটাবেসে এগুলো ঠিকঠাক দুটি আলাদা টেবিলে থাকে, তাই schema 4NF-এ। সমস্যা হয় যখন কোয়েরি দুটিকে একসাথে join করে: customers JOIN orders JOIN addresses. ফলের সারি-সংখ্যা হয় অর্ডার × ঠিকানা — ঠিক 4NF-লঙ্ঘনকারী টেবিলের গুণফল, যা আমরা সচেতনভাবে এড়িয়েছিলাম, এবার কোয়েরির ফলে ফিরে এসেছে।
৫০টি অর্ডার আর ৪টি ঠিকানা হলে ২০০টি সারি আসে, যেখানে দরকারি তথ্য ৫৪টি সারির। দুটি তালিকা আরও বড় হলে নেটওয়ার্কে আর মেমরিতে বিস্ফোরণ। ORM জগতে এটি পরিচিত সমস্যা: Entity Framework Core-এর ডকুমেন্টেশন একে “cartesian explosion” বলে, আর সমাধান হিসেবে প্রতিটি তালিকা আলাদা কোয়েরিতে আনার (split query) সুবিধা দেয়। Hibernate-ও একাধিক তালিকা একসাথে eager-fetch করার ক্ষেত্রে নির্দিষ্ট সীমাবদ্ধতা আর সতর্কতা দেখায়।
শিক্ষাটি দ্বিমুখী। এক, normalization শুধু টেবিলের ডিজাইন নয়, কোয়েরির ফলের আকারও বোঝায়। দুটি স্বাধীন তালিকাকে এক ফলে মেশালে MVD-র গুণফল ফিরে আসে। দুই, অনেক সময় AI বা ORM-এর বানানো একটি “সুন্দর” কোয়েরি ঠিক ফল দেয়, কিন্তু আড়ালে গুণফল টেনে আনে। তাই সারি-সংখ্যা দেখা আর ORM-এর তৈরি SQL লগে পড়া (পর্ব ৫০) জরুরি অভ্যাস।
মূল উপলব্ধি: 4NF শেখায় স্বাধীন তালিকা আলাদা রাখতে — টেবিলে যেমন, কোয়েরির ফলেও তেমন।
প্র ০৪5NF-এর চক্রাকার নিয়ম বাস্তবে এত বিরল কেন? আর একটি ternary টেবিলকে তিন টুকরোয় ভাঙার আগে ঠিক কী নিশ্চিত হতে হবে?
Join dependency *(SP, PJ, SJ) বলে: তিনটি জোড়া-সম্পর্ক জানলেই পুরো ত্রয়ী সম্পর্ক জানা যায়। এর পেছনে একটি শক্ত ব্যবসায়িক নিয়ম লাগে — “S যদি P দেয়, P যদি J-এ লাগে, আর S যদি J-এ কিছু দেয়, তবে S অবশ্যই J-এ P দেয়”। বাস্তবের বেশিরভাগ ত্রয়ী সম্পর্কে এমন নিয়ম থাকে না। একজন সরবরাহকারী একটি প্রকল্পে শুধু সিমেন্ট দিতে পারেন, যদিও তিনি অন্য প্রকল্পে রডও দেন আর এই প্রকল্পেও রড লাগে। তখন ত্রয়ীটি সত্যিই অবিভাজ্য, আর তিন-কলামের টেবিলই সঠিক 5NF ডিজাইন।
এ কারণে ER ডিজাইনে একটি পুরোনো সতর্কবাণী আছে (পর্ব ৭ ও ৮): একটি ternary relationship-কে তিনটি binary relationship দিয়ে বদলানো যায় না, যদি না চক্রাকার নিয়মটি সত্যি খাটে। Fagin ১৯৭৯ সালে PJNF সংজ্ঞায়িত করে দেখান, কোন অবস্থায় এমন ভাঙন বৈধ। ঝুঁকি দুই দিকেই: নিয়ম না থাকলে ভাঙলে ভুয়া সারি; আর নিয়ম থাকলে না ভাঙলে redundancy আর update anomaly.
নিশ্চিত হওয়ার পথ: ডেটা নয়, ব্যবসার মানুষকে প্রশ্ন। “S1 যদি P1 দেয় আর J1-এ কাজ করে, আর J1-এ P1 লাগে, তাহলে কি S1 নিশ্চিতভাবেই J1-এ P1 দেবে?” উত্তর “সাধারণত হ্যাঁ” হলে তা নিয়ম নয়। তারপর অংশ ৭-এর এডিটরের মতো তিন-টুকরোর join করে সারি গুনুন; একটি বাড়তি সারিই নিয়মটি খণ্ডন করে।
মূল উপলব্ধি: 5NF সমস্যা বিরল কারণ চক্রাকার নিয়ম বিরল; কিন্তু সেই নিয়মের অস্তিত্ব প্রমাণের দায় ডিজাইনারের, আর তা আসে ব্যবসা থেকে, ডেটা থেকে নয়।
প্র ০৫6NF-কে অনেকে “অবাস্তব” বলেন, অথচ সময়ভিত্তিক ডেটা আর কিছু আধুনিক স্টোরেজে এর ছায়া দেখা যায়। কোথায়, আর কেন?
6NF-এ প্রতিটি টেবিল সাধারণত একটি key আর একটি মাত্র তথ্য রাখে: (emp_id, salary, valid_from, valid_to), (emp_id, designation, valid_from, valid_to) — এভাবে। প্রচলিত OLTP অ্যাপে এটি অবাস্তব শোনায়, কারণ একজন কর্মচারীর পুরো তথ্য পেতে অনেকগুলো join লাগে। কিন্তু সময়ভিত্তিক ডেটায় যুক্তিটি জোরালো। বেতন বছরে একবার বদলায়, পদবি কয়েক বছরে, শাখা হয়তো কখনো না। সব এক টেবিলে রাখলে প্রতিটি বদলে পুরো সারির নতুন সংস্করণ লাগে, আর “কোন সময় কোন তথ্য সত্য ছিল” প্রশ্ন জটিল হয়ে যায়। Date, Darwen আর Lorentzos ঠিক এই সমস্যা থেকে 6NF-এর সংজ্ঞা দেন।
ছায়াটি দেখা যায় কয়েক জায়গায়। SQL:2011 standard সময়ভিত্তিক টেবিলের (system-versioned আর application-time period) বৈশিষ্ট্য যোগ করে, আর SQL Server ২০১৬ সংস্করণ থেকে system-versioned temporal table সমর্থন করে। Data warehouse-এ anchor modeling-এর মতো কিছু পদ্ধতি ইচ্ছে করে প্রায়-6NF নকশা ব্যবহার করে, যাতে নতুন attribute যোগ করা মানে নতুন টেবিল, পুরোনো টেবিলে হাত নয়। আর column-oriented স্টোরেজ প্রতিটি কলাম আলাদা জমা রাখে — যুক্তিগতভাবে না হলেও ভৌতিকভাবে এটি 6NF-এর মতো ভাঙন।
দাম অবশ্য বাস্তব: অনেক join, আর প্রতিটি টেবিলের সময়-সীমা মিলিয়ে কোয়েরি লেখা কঠিন। তাই 6NF সাধারণত পুরো ডেটাবেসে নয়, শুধু ইতিহাস রাখতে হবে এমন অংশে বা warehouse-এর বিশেষ স্তরে ব্যবহৃত হয়।
মূল উপলব্ধি: কোনো normal form চিরকাল “অবাস্তব” নয়; যখন সমস্যার আকার বদলায় — যেমন প্রতিটি তথ্যের নিজস্ব সময়রেখা — তখন কঠিনতম রূপটিই স্বাভাবিক সমাধান হয়ে ওঠে।
অনুশীলন
খাতা-কলম নিন। দুটি হাতে হিসাব, একটি কোয়েরি লেখা, একটি ডিজাইন।
-
হাতে trace করুন: R(A, B, C, D, E), F = {A → B, BC → D}. (ক) candidate key কী? (খ) A → B ধরে শুরু করে BCNF-এ ভাঙুন — প্রতিটি টুকরোয় projected FD দেখে। (গ) BC → D কি রক্ষিত?
(ক) A, C, E কোনো FD-র ডান দিকে নেই, তাই key-তে থাকবেই। ACE⁺ = {A, C, E, B, D} = R. তাই একমাত্র key {A, C, E}.
(খ) A⁺ = {A, B} — লঙ্ঘন। R1 = (A, B), R2 = (A, C, D, E). R2-তে AC⁺ ∩ R2 = {A, C, D}, অর্থাৎ AC → D খাটে, আর AC এই টুকরোর superkey নয় (key ACE) — লঙ্ঘন। R21 = (A, C, D), R22 = (A, C, E). চূড়ান্ত: (A, B), (A, C, D), (A, C, E) — প্রতিটির key যথাক্রমে A, AC, ACE.
(গ) result {B, C} থেকে শুরু: (A, B)-এর সাথে ছেদ {B}, B⁺ = {B}; (A, C, D)-এর সাথে ছেদ {C}, C⁺ = {C}; (A, C, E)-এর সাথেও {C}. কিছু বাড়ে না — BC → D হারিয়েছে। AC → D রক্ষিত, কিন্তু সেটি BC → D-র সমান নয়।
-
কোয়েরি লিখুন: teacher_skills(teacher, subject, language) টেবিলে কিছু সংমিশ্রণ বাদ পড়েছে। প্রতিটি শিক্ষকের জন্য যেসব (subject, language) জোড়া থাকা উচিত কিন্তু নেই, তাদের তালিকা দেয় এমন একটি কোয়েরি লিখুন। অংশ ৬-এর প্রথম এডিটরে (Hindi যোগ করার পর) চালালে কী আসবে?
SELECT DISTINCT a.teacher, a.subject, b.language FROM teacher_skills a JOIN teacher_skills b ON b.teacher = a.teacher EXCEPT SELECT teacher, subject, language FROM teacher_skills;Self join একই শিক্ষকের প্রতিটি বিষয়কে প্রতিটি ভাষার সাথে জোড়ে — MVD যে পূর্ণ গুণফল দাবি করে, ঠিক সেটি। তা থেকে আসল সারিগুলো বাদ দিলে থাকে অনুপস্থিতরা। Hindi যোগের পর ফল দুটি সারি:
Karim | AI | HindiআরKarim | Networks | Hindi. এগুলো INSERT করলে MVD আবার টিকে যায় — কিন্তু আসল সমাধান 4NF-এ ভাঙা, যাতে এই কোয়েরিই দরকার না হয়। -
ফল আগে বলুন: R(A, B, C, D), F = {A → B, C → D}. ভাঙন {(A, B), (A, C), (C, D)}. এটি কি lossless? Dependency preserving? প্রতিটি টুকরো কি BCNF-এ?
ধাপে ভাবুন। R-কে (A, B) আর (A, C, D)-তে ভাঙা: ছেদ {A}, A → B, তাই lossless. তারপর (A, C, D)-কে (A, C) আর (C, D)-তে: ছেদ {C}, C → D, তাই lossless. Lossless ভাঙনের টুকরোকে lossless-ভাবে ভাঙলে পুরোটাও lossless (chase চালালেও R2-এর সারি পুরো a হয়)।
A → B পুরোপুরি (A, B)-এর ভেতরে, C → D পুরোপুরি (C, D)-এর ভেতরে — dependency preserving. (A, B)-এর key A, (C, D)-এর key C, (A, C)-তে কোনো non-trivial FD নেই, key AC — তিনটিই BCNF. খেয়াল করুন, R-এর key {A, C} একটি টুকরো হিসেবেই আছে — 3NF synthesis-এর key-টেবিলের নিয়মের সাথে মিলে যায়।
-
ডিজাইন করুন: একটি অনলাইন দোকান-প্ল্যাটফর্মের টেবিল delivery(shop, product, area): কোন দোকান কোন পণ্য কোন এলাকায় পৌঁছে দেয়। দুটি বাস্তবতা ভাবুন — (ক) প্রতিটি দোকান তার সব পণ্য তার সব এলাকায় পাঠায়; (খ) কিছু পণ্য (যেমন হিমায়িত মাছ) শুধু কাছের এলাকায় যায়। প্রতিটির জন্য সঠিক schema কী?
(ক) পণ্যের তালিকা আর এলাকার তালিকা দোকানভেদে পরস্পর-স্বাধীন: shop →→ product | area. shop superkey নয়, তাই 4NF ভাঙে। Schema: shop_product(shop, product) আর shop_area(shop, area), দুটির key দুই কলাম মিলে, shop দিয়ে shops টেবিলের foreign key. নতুন এলাকা যোগ মানে একটি সারি, সব পণ্যের জন্য আলাদা সারি নয়।
(খ) এখন পণ্য আর এলাকা স্বাধীন নয় — কোনো MVD নেই। তিন-কলামের delivery(shop, product, area) টেবিলই সঠিক, key তিনটি কলাম মিলে; ভাঙলে “হিমায়িত মাছ দূরের এলাকায় যায় না” তথ্যটি হারাবে, join ভুয়া সারি দেবে। একটি মিশ্র নকশাও সম্ভব: সাধারণ পণ্যের জন্য (ক)-এর দুই টেবিল, আর ব্যতিক্রমের জন্য একটি product_area_restriction টেবিল — তবে তখন কোয়েরির যুক্তি জটিল হয়, তাই সিদ্ধান্ত নিন ব্যতিক্রম কত বেশি তা দেখে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি টেবিলকে BCNF-এ ভাঙার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- ব্যবসার নিয়ম থেকে FD-গুলো লেখা আর candidate key বের করা
- এমন একটি FD X → Y খোঁজা, যেখানে X superkey নয়
- X⁺ হিসাব করে টেবিলকে (X⁺) আর (R − (X⁺ − X))-এ ভাঙা
- প্রতিটি টুকরোয় projected FD দেখে আবার লঙ্ঘন খোঁজা
- শেষে প্রতিটি FD-র dependency preservation যাচাই করা
R(A, B, C)-তে FD-সেট {AB → C, C → B}. R সর্বোচ্চ কোন normal form-এ আছে?
- শুধু 1NF
- 2NF, কিন্তু 3NF নয়
- 3NF, কিন্তু BCNF নয়
- BCNF
R(A, B, C)-তে একমাত্র non-trivial FD হলো B → C. নিচের কোন ভাঙনটি lossless?
- (A, B) আর (A, C)
- (A, B) আর (B, C)
- (A, C) আর (B, C)
- (A) আর (B, C)
BCNF decomposition অ্যালগরিদম সম্পর্কে কোন কথাটি সবসময় সত্য?
- ফল সবসময় lossless আর dependency preserving
- ফল lossy হতে পারে, কিন্তু সব FD রক্ষা পায়
- ফল সবসময় 3NF synthesis-এর ফলের সমান
- ফল সবসময় lossless, কিন্তু কোনো FD হারাতে পারে
একটি টেবিল r-কে দুটি projection-এ ভেঙে আবার natural join করা হলো। join-এর ফল J. নিচের কোনটি সব ভাঙনের জন্য সত্য?
- J-তে r-এর সব সারি থাকে; lossy হলে বাড়তি সারিও থাকে
- Lossy হলে J-তে r-এর চেয়ে কম সারি থাকে
- J সবসময় r-এর সমান
- সাধারণ attribute key না হলে J ফাঁকা হয়
teacher_skills(teacher, subject, language)-এ প্রত্যেক শিক্ষকের বিষয়ের তালিকা আর ভাষার তালিকা পরস্পর-স্বাধীন, আর কোনো FD নেই। টেবিলটি সম্পর্কে কোনটি ঠিক?
- BCNF ভাঙে, কারণ teacher → subject
- BCNF-এ আছে, কিন্তু 4NF ভাঙে; (teacher, subject) আর (teacher, language)-এ ভাঙা উচিত
- 4NF-এ আছে, কোনো redundancy নেই
- (teacher, subject) আর (subject, language)-এ ভাঙা উচিত
supplier–part–project টেবিলের তিনটি জোড়া-projection-এর যেকোনো দুটি join করলে ভুয়া সারি আসে, কিন্তু তিনটি একসাথে join করলে ঠিক মূল টেবিল ফেরত আসে। কোনো FD বা non-trivial MVD নেই। টেবিলটি কোন normal form ভাঙে?
- BCNF
- 4NF
- 5NF
- 1NF
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- BCNF: প্রতিটি non-trivial X → Y-তে X superkey. 3NF বাড়তি ছাড় দেয় — ডান দিক prime হলে চলে। BCNF ⇒ 3NF, উল্টোটা নয়।
- Lossless: R₁ ∩ R₂ → R₁ বা → R₂; তিন বা বেশি টুকরোয় chase. Lossy join সারি হারায় না, ভুয়া সারি যোগ করে।
- Dependency preservation: প্রতিটি FD টুকরোগুলোর ভেতর থেকেই বের করা যায় কি না; না গেলে নিয়ম রক্ষায় join বা trigger লাগে।
- BCNF ভাঙন সবসময় lossless, কিন্তু FD হারাতে পারে; 3NF synthesis দুটিই দেয়। শিক্ষার্থী–কোর্স–শিক্ষকে দ্বন্দ্ব অনিবার্য।
- MVD → 4NF, JD → 5NF: স্বাধীন তালিকা আলাদা টেবিলে; চক্রাকার নিয়ম থাকলে তিন টুকরো। বাস্তবের লক্ষ্য সাধারণত 3NF/BCNF.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৩৮ · Denormalization — কখন নিয়ম ভাঙা যায়পরবর্তী পর্বমডিউল ৫-এর শেষ: normalization-এর দাম আর ইচ্ছাকৃত ব্যতিক্রম।
- পর্ব ৩৬ · Normalization — 1NF, 2NF আর 3NFআগের পর্বpartial আর transitive dependency, যেখান থেকে এই পর্ব শুরু।
- পর্ব ৩৫ · Functional dependency — closure, key আর canonical coverভিত্তিclosure আর canonical cover — এই পর্বের প্রতিটি হিসাবের হাতিয়ার।
- ডেটাবেস (DBMS) ও SQL · কোর্স হোম৬৭টি পর্বপুরো কোর্সের রোডম্যাপ — ১০টি মডিউল।
- Python · SQLAlchemy ORMহাতে-কলমেnormalized schema-কে ORM-এ মডেল করা, আর একাধিক তালিকা একসাথে আনার ফাঁদ।