Database Normalization1NF2NF3NFRelational DatabaseBackend Engineering

Day 9 — Database Normalization — 1NF থেকে 3NF

June 15, 20265 min read30 Days Of Backend Engineering

আপনাকে একটা ছোট প্রশ্ন করি? মনে করুন আপনার এনআইডি (NID) কার্ডের নাম পরিবর্তন করা লাগবে, তখন কী কী ডকুমেন্ট লাগবে সেটা তো ধারণা করতে পারছেন? এর থেকেও বড় কথা, আপনি যে নামটা পরিবর্তন করলেন, এখন কিন্তু আপনার যত সরকারি নথিপত্র (সার্টিফিকেট, ড্রাইভিং লাইসেন্স ইত্যাদি) আছে সেগুলোতেও কিন্তু নাম পরিবর্তন করা লাগবে। না হলে কিন্তু ঝামেলা হবে। প্রথম দিকে একটু কঠিন লাগতে পারে কিন্তু একবার অভ্যস্ত হয়ে গেলে দেখবেন ডাটাবেজ নরমালাইজ করাটা অনেক সহজ মনে হচ্ছে।

আপনি যে নাম পরিবর্তন করবেন সেটাও সরকারি ডকুমেন্ট আর বাকি যেগুলোতে আপডেট করতে হবে, সেগুলোও কিন্তু সরকারি ডকুমেন্ট। এখন এক জায়গায় পরিবর্তন করার জন্য সব জায়গাতেই পরিবর্তন করতে হবে। এখানে অনেক সময় নষ্ট হবে। আর এই দেশের সরকারি অফিসের কাজ কেমন, সেটা যদি আপনি কোনো কাজ করার জন্য কোনোদিন সরকারি অফিসে যান তাহলে বুঝবেন।

এটা কিন্তু একটা সমস্যা, এখন এই সমস্যাটার কি সমাধান নাই? একটা সমাধান হতে পারে, যে একজায়গায় সম্পর্কিত তথ্য যেমন—নাম, ঠিকানা, ব্লাড গ্রুপ (এই তথ্যগুলো কিন্তু সব জায়গায় একই থাকে), এগুলো একটা সেন্ট্রাল জায়গায় থাকবে। সেখান থেকে সব ডকুমেন্ট-এ সরবরাহ হবে।

ওপরের যে সমস্যাটার কথা বললাম, এই সমস্যাটাকে বলে Data Redundancy। আর সমাধান যে বললাম সেটা হচ্ছে Normalization


Database Normalization কী?

Database Normalization হলো রিলেশনাল ডাটাবেজে (Relational Database) তথ্য সুশৃঙ্খলভাবে সাজানোর একটি পদ্ধতি। এর মাধ্যমে বড় ও জটিল টেবিলগুলোকে ভেঙে ছোট এবং সম্পর্কযুক্ত টেবিলে রূপান্তর করা হয়। এর ফলে ডাটাবেজে Data Redundancy কমে এবং ডেটা নষ্ট হওয়ার ঝুঁকি দূর হয়।

Normalization-এর প্রধান উদ্দেশ্যগুলো হলো:

  • Data Redundancy কমানো: একই তথ্য বারবার সংরক্ষণ করা বন্ধ করা। ওপরের উদাহরণ অনুযায়ী, সব জায়গায় একই information বারবার repeat হচ্ছে। একটা information update করার জন্য multiple জায়গায় information update করতে হচ্ছে। Normalization করলে এই redundancy চলে যাবে।
  • Inconsistency দূর করা: Multiple জায়গায় data থাকলে, সব জায়গায় data update করতে হবে। এখন কোনো কারণে যদি কোনো একটা operation fail করে তাহলে কিন্তু data-র inconsistency (অসঙ্গতি) দেখা দিবে।
  • Data Integrity নিশ্চিত করা: একটা information একজায়গায় থাকলে information ভুল হওয়ার সমস্যা থাকে না।

কেন Normalization দরকার?

নিচের (order table)-টা একটু খেয়াল করুন। এখানে খেয়াল করলে দেখবেন John Doe নামের একটা user ৩টা different product কিনেছে।

order_idcustomer_namecustomer_emailproduct_nameproduct_price
1John Doem@gmail.comLaptop50000
2John Doem@gmail.comMouse500
3John Doem@gmail.comKeyboard1200
এখন কোনো কারণে যদি **John Doe**-এর email বা name change হয় তাহলে কিন্তু যেখানে যেখানে এই email বা name use হয়েছে সেখানে সেখানে এই name বা email update করা লাগবে। একটা information update করার জন্য ৩টা row update করতে হচ্ছে। এই সমস্যাটার নাম হচ্ছে **Update Anomaly**।

এখানে normalization করলে এই problem-টা solve হবে। Normalization-এর অনেকগুলো step আছে: 1NF, 2NF, 3NF। নিচে এগুলো example-সহ বিস্তারিত আলোচনা করা হয়েছে।


First Normal Form (1NF): একটা cell, একটা value

মনে করুন, আপনি আপনার family-এর সাথে shopping করতে গেছেন, এখন আপনি ৩টা জিনিস কিনেছেন। আপনার ছোট ভাই কিনেছে ২টা জিনিস।

useritems
Titu (you)Laptop, Mouse, Keyboard
Riju (little bro)Headphone, T-Shirt
এখানে একটা operation করতে হবে, suppose Keyboard-টা বাদ দিতে হবে। তখন কী করতে হবে? List-কে iterate করে delete করতে হবে। এখানে একটা extra time নষ্ট হচ্ছে। এখানে simple list দেয়া আছে। এখন যদি price, quantity এগুলো থাকতো তাহলে query কতটা problematic হতে পারতো? Just imagine।

এখানে 2nd column খেয়াল করুন, এখানে Laptop, Mouse, Keyboard মোট ৩টা value store হচ্ছে। একটা value-এর বদলে একটা list রয়েছে। আর 1NF বলে, এই কাজটা করা যাবে না। একটা value-ই থাকবে, কোনো list থাকবে না। এর বদলে ৩টা row বানাতে হবে। যে কয়টা item থাকবে সেই কয়টা row থাকবে। এখন 1NF অনুযায়ী নিচের মতো হবে:

useritems
Titu (you)Laptop
Titu (you)Mouse
Titu (you)Keyboard
Riju (little bro)Headphone
Riju (little bro)T-Shirt
এখানে বলতে পারেন যে, এখানে তো data duplicate হচ্ছে। আসলে বিষয় হচ্ছে আপনাকে বোঝানোর জন্য just name use করেছি। কিন্তু actual database-এ foreign key use করা হয়। User table-এর সাথে just simply connect করে দেয়া থাকে। এই জন্য data duplication-এর কোনো problem-ই হবে না।

Second Normal Form (2NF): সব column primary key-এর উপর dependent হতে হবে

order_id (PK)product_id (PK)customer_nameproduct_namequantity
101P-1RahimKeyboard2
101P-2RahimMouse1
102P-1KarimKeyboard3
102P-3KarimMonitor1
এখানে primary key হচ্ছে `(order_id, product_id)`, composite primary key।

এখানে একটা জিনিস খেয়াল করুন, customer_name জানার জন্য শুধুমাত্র order_id-এর ওপরেই rely করে, order পেলেই name জানা যাবে। অর্থাৎ customer_name primary key-এর ওপর totally dependent না। Same জিনিসটা product_name-এর ক্ষেত্রেও প্রযোজ্য। এটা একটা problem। এটাকে বলে Partial Dependency

আসলে problem-টা হচ্ছে এখানে Data Redundancy হচ্ছে। এটা হতে দেয়া যাবে না। আর তা যাতে না হয়, সেটাই হচ্ছে 2NF। শুদ্ধ ভাষায় বললে:

**প্রতিটি non-key column-কে পুরো primary key-এর উপর fully dependent হতে হবে — key-এর শুধু একটা অংশের উপর নয়।**

একটা table 2NF-এ থাকার শর্ত দুইটা:

  1. টেবিলটা আগে 1NF-এ থাকতে হবে (atomic values, no repeating groups)।
  2. Partial Dependencies দূর করা।
ওপরের এই ২টা condition fulfill করলেই এটা 2NF হয়ে যাবে।

তবে এখানে একটা catch আছে। এই 2NF-টা তখনই valid হবে যখন কোনো table-এ composite primary key থাকে। Partial dependencies resolve করলেই 2NF form হয়ে যাবে। তবে কোনো table-এ যদি single primary key থাকে তাহলে সেখানে partial dependencies হওয়ার possibility থাকে না। সেটা automatically 2NF format-এই থাকে।

Example: একটা Order System

ধরা যাক আমরা order-এর line item একটা টেবিলে রাখছি। একটা order-এ একাধিক product থাকতে পারে, তাই key হলো (order_id, product_id) — composite।

#### ❌ 2NF Violation (খারাপ design)

order_items table

order_id (PK)product_id (PK)customer_nameproduct_namequantity
101P-1RahimKeyboard2
101P-2RahimMouse1
102P-1KarimKeyboard3
102P-3KarimMonitor1
এখানে primary key হচ্ছে `(order_id, product_id)`, composite primary key।

এখন dependency-গুলো খেয়াল করুন:

  • customer_name → শুধু order_id-এর উপর depend করে। অর্থাৎ order_id জানলেই customer name জানা যাবে। আর এখানে order_id একটা partial key। এর জন্য এটা একটা partial dependency
  • product_name → শুধু product_id-এর উপর depend করে। অর্থাৎ product_id জানলেই product name জানা যাবে। আর এখানে product_id-ও একটা partial key। এর জন্য এটাও একটা partial dependency
  • quantity → এটা full dependency। কারণ একটা specific order-এ একটা specific product কতটা কিনেছে তা জানার জন্য সম্পূর্ণ key (order_id, product_id) লাগে।

এই design-এর সমস্যা কী?

  • Data Redundancy: রহিম-এর নাম দুইবার লেখা, "Keyboard" নামটাও বারবার repeat হচ্ছে।
  • Update Anomaly: রহিম তার নাম বদলালে একাধিক row update করতে হবে — একটা মিস হলেই data inconsistent।
  • Insert Anomaly: নতুন একটা product (যেটা কেউ এখনো order করেনি) database-এ রাখতে পারবে না, কারণ order_id ছাড়া row insert হবে না।
  • Delete Anomaly: কোনো order-এর সব row delete করলে customer বা product-এর তথ্যও হারিয়ে যাবে।

✅ 2NF Solution (সঠিক design)

Solution একদম easy, partial dependencies-এর জন্য আলাদা dedicated table বানাতে হবে। সেখানে এই data-গুলো insert করে দিলেই হয়ে যাবে। এখন main table-এ redundancy completely remove হয়ে যাবে।

orders tablecustomer_name এখন order_id-এর সাথেই থাকছে

order_id (PK)customer_name
101Rahim
102Karim
**`products` table** — `product_name` এখন `product_id`-এর সাথে
product_id (PK)product_name
P-1Keyboard
P-2Mouse
P-3Monitor
**`order_items` table** — শুধু পুরো key-এর ওপর dependent column থাকছে
order_id (PK, FK)product_id (PK, FK)quantity
101P-12
101P-21
102P-13
102P-31
এখন:
  • Rahim-এর নাম একবারই আছে — নাম বদলালে একটা জায়গায় update করলেই হবে।
  • নতুন product order ছাড়াই products table-এ insert করা যাবে।
  • কোনো order delete করলেও customer আর product info নিরাপদ থাকবে।
  • order_items-এর প্রতিটা column (এখানে শুধু quantity) পুরো key (order_id, product_id)-এর উপর fully dependent। ✅

Third Normal Form (3NF): Transitive Dependency বাদ দাও

বড় করে বলে বোঝানো যাক। মনে করুন key → A আর A → B। তাহলে ঘুরে ফিরে key → B হয়, কিন্তু B actually key-এর ওপর directly depend করছে না — সে depend করছে A-এর ওপর, যেটা নিজেও একটা non-key column। এই indirect dependency-টাই হচ্ছে transitive dependency

শুদ্ধ ভাষায় বললে:

**কোনো non-key column যেন আরেকটা non-key column-এর উপর dependent না হয় — প্রতিটা non-key column directly primary key-এর উপর depend করবে।**

একটা table 3NF-এ থাকার condition দুইটা:

  1. Table-টা আগে 2NF-এ থাকতে হবে (no partial dependency)।
  2. কোনো transitive dependency থাকবে না — অর্থাৎ key → A → B এমন chain চলবে না, যেখানে B non-key হয়ে আরেকটা non-key A-এর ওপর ঝুলে আছে।
ওপরের এই ২টা condition fulfill করলেই এটা 3NF হয়ে যাবে।

Example: একটা Employee System

emp_id (PK)emp_namedept_iddept_namedept_location
E-01RahimD-1EngineeringDhaka
E-02KarimD-1EngineeringDhaka
E-03SumiD-2MarketingChittagong
E-04JamalD-2MarketingChittagong
এখানে dependency chain-টা খেয়াল করুন:
  • emp_iddept_id: একজন employee একটা specific department-এ থাকে। ✅ directly depend করছে।
  • dept_iddept_name, dept_location: একটা department-এর name আর location ঠিক করে দেয় dept_id, employee দিয়ে তাতে কিছু আসে যায় না।
  • ফলে emp_iddept_iddept_name — অর্থাৎ dept_name আর dept_location transitively dependent। 🚨 এরা একটা non-key column (dept_id)-এর ওপর ঝুলে আছে, key-এর ওপর directly না।
#### ❌ এই design-এর সমস্যা কী?
  • Data Redundancy: "Engineering, Dhaka" বারবার repeat হচ্ছে — যত employee সেই department-এ, ততবার।
  • Update Anomaly: Engineering department Dhaka থেকে Khulna-তে shift করলে একাধিক row update করতে হবে। একটা miss হলে এই same department-এর দুইরকমের location দেখাবে — inconsistent।
  • Insert Anomaly: নতুন একটা department (যেখানে এখনো কোনো employee নেই) database-এ রাখা যাবে না, কারণ emp_id ছাড়া row insert হয় না।
  • Delete Anomaly: কোনো department-এর শেষ employee-কে delete করলে সেই department-এর info-ও (name, location) হারিয়ে যাবে।
#### ✅ 3NF Solution (সঠিক design)

Solution একদম easy, transitively dependent column-গুলোকে আলাদা dedicated table-এ সরাতে হবে। যে non-key column (dept_id) এদেরকে determine করছে, সেটাকে নতুন table-এর primary key বানাতে হবে। এখন main table-এ redundancy completely remove হয়ে যাবে।

departments tabledept_name আর dept_location এখন তাদের actual determinant dept_id-এর সাথে

dept_id (PK)dept_namedept_location
D-1EngineeringDhaka
D-2MarketingChittagong
**`employees` table** — শুধু `emp_id`-এর ওপর directly dependent column থাকছে, `dept_id` থাকছে foreign key হিসেবে
emp_id (PK)emp_namedept_id (FK)
E-01RahimD-1
E-02KarimD-1
E-03SumiD-2
E-04JamalD-2
এখন:
  • "Engineering, Dhaka" একবারই আছে — department shift করলে একটা জায়গায় update করলেই হবে।
  • নতুন department employee ছাড়াই departments table-এ insert করা যাবে।
  • শেষ employee delete করলেও department-এর info নিরাপদ থাকবে।
  • employees-এর প্রতিটা non-key column (emp_name, dept_id) directly emp_id-এর ওপর depend করছে — কোনো ঘুরপথ নেই। ✅

Over-Normalization: যেখানে ক্যাচালটা লাগে!

সব কিছু normalize করলেই কি ভালো? একদমই না! বেশি normalization মানে বেশি JOIN। বেশি JOIN মানে slow query। একটা report generate করতে ১০টা table JOIN করতে হচ্ছে। এটা performance nightmare।

অনেক সময় usage ও requirement-এর ওপর নির্ভর করে intentionally Denormalization করা হয়ে থাকে যাতে সিস্টেমে unnecessary operations কমানো যায়। এ ছাড়াও read-heavy সিস্টেমেও intentionally controlled denormalization করা হয়ে থাকে।


বটম লাইন

একটা জিনিস খেয়াল করবেন, আপনি যদি অনেক দিন ধরে backend engineer হিসেবে কাজ করেন তাহলে আপনি ওপরে যা যা বললাম তার বেশির ভাগ জিনিসই আপনি না জেনেই apply করে ফেলেছেন। আসলে আপনি ডাটাবেসটা properly design করলে আপনার এই normalization-এর formality নিয়ে প্যারা খেতে হবে না। আপনার logic-ই আপনাকে ডাটাবেস normalize করতে help করবে।

আর সত্য বলতে আমি নিজেই এই রকম সিচুয়েশনে পড়েছি। এই database normalization নিয়ে পড়াশোনা করার আগেই এই প্রবলেম সলিউশন করেছি।

আপনি কখনো ডাটাবেস normalize না করার জন্য প্যারা খেয়েছেন? অবশ্যই শেয়ার করুন! 👇