DatabaseDatabase

ER model, SQL, relational algebra, normalization, transactions, and indexing — the full database package for the exam. ER model, SQL, relational algebra, normalization, transaction আর indexing — exam-এর জন্য database-এর পুরো package।

1. ER Model1. ER Model

The ER model (Entity-Relationship model) is a way to draw a picture of your data before you build the database. You describe the real world using entities, attributes, and relationships. Then you turn the picture into tables.

ER model (Entity-Relationship model) হলো database বানানোর আগে data-র একটা ছবি আঁকার পদ্ধতি। বাস্তব জগতকে আমরা entity, attribute আর relationship দিয়ে describe করি। পরে এই ছবি থেকে table বানানো হয়।

Entity and entity setEntity আর entity set

An entity is a real thing we store data about. Example: a student named Rahim. An entity set is the group of all similar entities. Example: all students. In the diagram, an entity set is a rectangle.

Entity হলো বাস্তব একটা জিনিস, যার data আমরা রাখি। যেমন: রহিম নামের একজন student। Entity set হলো একই ধরনের সব entity-র group। যেমন: সব student। Diagram-এ entity set আঁকা হয় rectangle দিয়ে।

Attribute typesAttribute-এর ধরন

An attribute is a property of an entity. It is drawn as an ellipse. There are several kinds:

  • Simple: cannot be divided. Example: age.
  • Composite: can be split into parts. Example: name = first_name + last_name.
  • Single-valued: one value per entity. Example: sid.
  • Multivalued: can have many values. Example: phone (a student may have 2 phones). Drawn as a double ellipse.
  • Derived: can be computed from other data. Example: age from date_of_birth. Drawn as a dashed ellipse.
  • Key attribute: uniquely identifies an entity. Example: sid. Its name is underlined.

Attribute হলো entity-র একটা property। এটা আঁকা হয় ellipse দিয়ে। কয়েক ধরনের attribute আছে:

  • Simple: ভাগ করা যায় না। যেমন: age
  • Composite: ছোট অংশে ভাগ করা যায়। যেমন: name = first_name + last_name।
  • Single-valued: প্রতি entity-তে একটাই value। যেমন: sid
  • Multivalued: অনেক value থাকতে পারে। যেমন: phone (একজন student-এর ২টা phone থাকতে পারে)। আঁকা হয় double ellipse দিয়ে।
  • Derived: অন্য data থেকে হিসাব করে বের করা যায়। যেমন: date_of_birth থেকে age। আঁকা হয় dashed ellipse দিয়ে।
  • Key attribute: entity-কে uniquely চেনায়। যেমন: sid। নামের নিচে underline দেওয়া হয়।

Relationships and cardinalityRelationship আর cardinality

A relationship connects two (or more) entity sets. It is drawn as a diamond. Example: Student Enrolls in Course. A relationship can also have its own attribute (like grade).

Cardinality tells how many entities can join the relationship on each side:

  • 1:1 (one-to-one): one entity matches at most one on the other side. Example: one department has one head; one teacher heads one department.
  • 1:N (one-to-many): one entity on the left matches many on the right. Example: one department has many students, but each student belongs to one department.
  • M:N (many-to-many): many on both sides. Example: one student takes many courses, and one course has many students.

Participation says whether every entity must join the relationship:

  • Total (full) participation: every entity takes part. Drawn with a double line. Example: every Enroll row must have a student.
  • Partial participation: some entities may not take part. Drawn with a single line. Example: some students may not enroll in any course yet.

Relationship দুটো (বা তার বেশি) entity set-কে যুক্ত করে। এটা আঁকা হয় diamond দিয়ে। যেমন: Student Enrolls in Course। Relationship-এর নিজেরও attribute থাকতে পারে (যেমন grade)।

Cardinality বলে দেয়, relationship-এর প্রতি পাশে কয়টা entity যুক্ত হতে পারে:

  • 1:1 (one-to-one): একটা entity অন্য পাশের সর্বোচ্চ একটার সাথে match করে। যেমন: এক department-এর এক head; এক teacher এক department-এর head।
  • 1:N (one-to-many): বাম পাশের একটা entity ডান পাশের অনেকগুলোর সাথে match করে। যেমন: এক department-এ অনেক student, কিন্তু প্রতিটা student এক department-এ।
  • M:N (many-to-many): দুই পাশেই অনেক। যেমন: এক student অনেক course নেয়, আর এক course-এ অনেক student থাকে।

Participation বলে দেয়, প্রতিটা entity-কে relationship-এ থাকতেই হবে কি না:

  • Total (full) participation: সব entity অংশ নেয়। আঁকা হয় double line দিয়ে। যেমন: প্রতিটা Enroll row-তে একজন student থাকতেই হবে।
  • Partial participation: কিছু entity অংশ না-ও নিতে পারে। Single line দিয়ে আঁকা হয়। যেমন: কোনো কোনো student এখনো কোনো course-এ enroll না-ও করতে পারে।

Weak entityWeak entity

A weak entity has no key of its own. It depends on a strong (owner) entity to be identified. It is drawn as a double rectangle, and the identifying relationship is a double diamond. Its partial key (discriminator) gets a dashed underline.

Example: Section(sec_no, day, time) of a Course. Section 1 of CSE101 and Section 1 of CSE201 both have sec_no = 1. So sec_no alone cannot identify a section. Full key of Section = owner key + partial key = (cid, sec_no).

Weak entity-র নিজের কোনো key থাকে না। চেনার জন্য এটা একটা strong (owner) entity-র উপর নির্ভর করে। আঁকা হয় double rectangle দিয়ে, আর identifying relationship আঁকা হয় double diamond দিয়ে। এর partial key (discriminator)-তে dashed underline দেওয়া হয়।

যেমন: Course-এর Section(sec_no, day, time)। CSE101-এর Section 1 আর CSE201-এর Section 1 — দুটোরই sec_no = 1। তাই শুধু sec_no দিয়ে section চেনা যায় না। Section-এর full key = owner-এর key + partial key = (cid, sec_no)।

ER diagram notation summaryER diagram notation-এর summary

SymbolSymbol Meaningঅর্থ
RectangleRectangleEntity setEntity set
Double rectangleDouble rectangleWeak entity setWeak entity set
EllipseEllipseAttributeAttribute
Double ellipseDouble ellipseMultivalued attributeMultivalued attribute
Dashed ellipseDashed ellipseDerived attributeDerived attribute
Underlined nameUnderlined নামKey attributeKey attribute
DiamondDiamondRelationshipRelationship
Double diamondDouble diamondIdentifying relationship (for weak entity)Identifying relationship (weak entity-র জন্য)
Double lineDouble lineTotal participationTotal participation
Example: University ER design.

Requirements: A university has departments. Each department has many students, but a student belongs to one department. Students enroll in courses; a course can have many students. Each enrollment has a grade. Courses have sections (weak entity).

  • Entities: Department(dno, dname), Student(sid, name), Course(cid, title, credit).
  • Weak entity: Section(sec_no, day) owned by Course.
  • Relationships: BelongsTo (Student–Department, N:1), Enrolls (Student–Course, M:N, with attribute grade), Has (Course–Section, identifying, 1:N).
Example: University ER design।

Requirement: একটা university-তে অনেক department আছে। প্রতি department-এ অনেক student, কিন্তু একজন student এক department-এ। Student-রা course-এ enroll করে; এক course-এ অনেক student। প্রতিটা enrollment-এ একটা grade থাকে। Course-এর section থাকে (weak entity)।

  • Entity: Department(dno, dname), Student(sid, name), Course(cid, title, credit)
  • Weak entity: Section(sec_no, day) — owner হলো Course।
  • Relationship: BelongsTo (Student–Department, N:1), Enrolls (Student–Course, M:N, attribute grade), Has (Course–Section, identifying, 1:N)।
Department dno dname BelongsTo 1 Student N sid name Enrolls M grade Course N cid title Section Has 1 N sec_no
University ER diagram: Department 1:N Student, Student M:N Course (with grade), Course 1:N Section (weak entity). University ER diagram: Department 1:N Student, Student M:N Course (grade সহ), Course 1:N Section (weak entity)।

ER → table conversion rulesER → table conversion-এর নিয়ম

  1. Strong entity: becomes a table. Attributes become columns. Key attribute becomes primary key. Student(sid, name) → table Student.
  2. Composite attribute: keep only the parts as columns (first_name, last_name), not the whole.
  3. Multivalued attribute: make a separate table. phone → table StudentPhone(sid, phone), key = (sid, phone).
  4. Derived attribute: usually not stored. Compute it when needed.
  5. Weak entity: table with owner's key + partial key as primary key. Section → Section(cid, sec_no, day), PK = (cid, sec_no), FK cid → Course.
  6. 1:1 relationship: put the key of one side as a foreign key in the other side (prefer the side with total participation), or merge the two tables.
  7. 1:N relationship: put the key of the "1" side as a foreign key in the "N" side. BelongsTo → add column dno in Student.
  8. M:N relationship: make a new table with both keys plus relationship attributes. Enrolls → Enroll(sid, cid, grade), PK = (sid, cid).
  1. Strong entity: একটা table হয়। Attribute-গুলো column হয়। Key attribute হয় primary key। Student(sid, name) → Student table।
  2. Composite attribute: পুরোটা না রেখে শুধু অংশগুলো column হিসেবে রাখুন (first_name, last_name)।
  3. Multivalued attribute: আলাদা table বানান। phoneStudentPhone(sid, phone) table, key = (sid, phone)।
  4. Derived attribute: সাধারণত store করা হয় না। দরকারে হিসাব করা হয়।
  5. Weak entity: owner-এর key + partial key মিলে primary key হয়। Section → Section(cid, sec_no, day), PK = (cid, sec_no), FK cid → Course।
  6. 1:1 relationship: এক পাশের key অন্য পাশে foreign key হিসেবে দিন (total participation যে পাশে, সেই পাশে দেওয়া ভালো), অথবা দুই table merge করুন।
  7. 1:N relationship: "1" পাশের key-টা "N" পাশে foreign key হিসেবে দিন। BelongsTo → Student table-এ dno column যোগ করুন।
  8. M:N relationship: দুই পাশের key আর relationship-এর attribute নিয়ে নতুন table বানান। Enrolls → Enroll(sid, cid, grade), PK = (sid, cid)।
Exam tip: The most asked rule: M:N always needs a new table; 1:N never does (just a foreign key on the N side). Also remember: weak entity PK = owner key + partial key.
Exam tip: সবচেয়ে বেশি জিজ্ঞেস করা নিয়ম: M:N হলে সবসময় নতুন table লাগে; 1:N হলে লাগে না (N পাশে শুধু foreign key দিলেই হয়)। আরও মনে রাখুন: weak entity-র PK = owner-এর key + partial key।

Worked example: full vs partial participation (Student–Course)Worked example: full vs partial participation (Student–Course)

Problem: Draw an ER diagram for: "Every student MUST enroll in at least one course, but a course may have no students yet." Then convert it to tables.

Step 1 — the diagram. Two entity sets: Student(sid, name) and Course(cid, title), joined by the relationship Enroll (M:N, with attribute grade).

  • Student side of Enroll: double line — total (full) participation, because every student appears in Enroll at least once.
  • Course side of Enroll: single line — partial participation, because a course with zero students is allowed.

Step 2 — the tables. M:N → a new junction table:

  • Student(sid, name)
  • Course(cid, title)
  • Enroll(sid, cid, grade), PK = (sid, cid), FK sid → Student, FK cid → Course

Step 3 — where does "full" go? The foreign keys enforce that Enroll rows point at real students and courses. But "every student has at least one Enroll row" cannot be written as a plain column constraint — state it as a separate rule (an assertion / application check). Saying this last line is what earns the participation marks.

Problem: এই কথার ER diagram আঁকুন: "প্রতিটা student-কে কমপক্ষে একটা course-এ enroll করতেই হবে, কিন্তু কোনো course-এ এখনো কোনো student না-ও থাকতে পারে।" তারপর table-এ convert করুন।

Step 1 — diagram। দুটো entity set: Student(sid, name) আর Course(cid, title), মাঝে relationship Enroll (M:N, attribute grade)।

  • Enroll-এর Student পাশ: double line — total (full) participation, কারণ প্রতিটা student অন্তত একবার Enroll-এ আছে।
  • Enroll-এর Course পাশ: single line — partial participation, কারণ শূন্য student-এর course allow করা হয়েছে।

Step 2 — table। M:N → নতুন junction table:

  • Student(sid, name)
  • Course(cid, title)
  • Enroll(sid, cid, grade), PK = (sid, cid), FK sid → Student, FK cid → Course

Step 3 — "full" কোথায় গেল? Foreign key নিশ্চিত করে Enroll-এর row আসল student আর course-কে point করে। কিন্তু "প্রতিটা student-এর কমপক্ষে একটা Enroll row আছে" — এটা সাধারণ column constraint দিয়ে লেখা যায় না; আলাদা rule হিসেবে বলতে হয় (assertion / application check)। এই শেষ লাইনটা বলতে পারলেই participation-এর নম্বর আসে।

Real BUET question (asked April 2017): an ER diagram where one side has FULL participation and the other has PARTIAL, then conversion to tables — exactly this Student–Course pattern. Remember: double line = full/total, single line = partial.
Real BUET question (asked April 2017): এক পাশে FULL participation আর অন্য পাশে PARTIAL — এমন ER diagram এঁকে table-এ convert করা; ঠিক এই Student–Course pattern-টাই। মনে রাখুন: double line = full/total, single line = partial।

2. Relational Model2. Relational Model

In the relational model, all data lives in relations (tables). This is the model SQL databases use.

  • Relation: a table. Example: Student.
  • Tuple: one row of the table. Example: (101, 'Rahim', 'CSE').
  • Attribute: one column. Example: name.
  • Domain: the set of allowed values for an attribute. Example: credit must be a positive number.
  • Degree (arity): number of attributes. Student(sid, name, dept) has degree 3.
  • Cardinality: number of tuples (rows) in the relation.

Schema notation: we write a relation as Student(sid, name, dept). The primary key is underlined: Student(sid, name, dept).

Relational model-এ সব data থাকে relation (মানে table)-এ। SQL database এই model ব্যবহার করে।

  • Relation: একটা table। যেমন: Student।
  • Tuple: table-এর একটা row। যেমন: (101, 'Rahim', 'CSE')।
  • Attribute: একটা column। যেমন: name
  • Domain: একটা attribute-এ কী কী value বসতে পারে, তার set। যেমন: credit অবশ্যই positive number।
  • Degree (arity): attribute-এর সংখ্যা। Student(sid, name, dept)-এর degree = 3।
  • Cardinality: relation-এ tuple (row)-এর সংখ্যা।

Schema notation: relation লেখা হয় এভাবে: Student(sid, name, dept)। Primary key-তে underline দেওয়া হয়: Student(sid, name, dept)

KeysKeys

  • Superkey: any set of attributes that uniquely identifies a tuple. Example: {sid}, {sid, name}, {sid, dept} are all superkeys of Student.
  • Candidate key: a minimal superkey — remove any attribute and it stops being a key. Example: {sid}. If email is also unique, {email} is another candidate key.
  • Primary key: the one candidate key we choose to identify rows. It can never be NULL.
  • Alternate key: a candidate key that was not chosen as primary. Example: {email}.
  • Composite key: a key made of more than one attribute. Example: (sid, cid) in Enroll.
  • Foreign key: an attribute in one table that points to the primary key of another table. Example: Enroll.sidStudent.sid.
  • Superkey: attribute-এর যেকোনো set যা একটা tuple-কে uniquely চেনায়। যেমন: {sid}, {sid, name}, {sid, dept} — সবগুলোই Student-এর superkey।
  • Candidate key: একটা minimal superkey — কোনো attribute বাদ দিলে আর key থাকে না। যেমন: {sid}। email-ও unique হলে {email} আরেকটা candidate key।
  • Primary key: candidate key-গুলোর মধ্যে যেটাকে আমরা বেছে নিই। এটা কখনো NULL হতে পারে না।
  • Alternate key: যে candidate key-টা primary হিসেবে বাছাই হয়নি। যেমন: {email}।
  • Composite key: একাধিক attribute মিলে বানানো key। যেমন: Enroll-এর (sid, cid)।
  • Foreign key: এক table-এর attribute যা অন্য table-এর primary key-কে point করে। যেমন: Enroll.sidStudent.sid
Example: In Enroll(sid, cid, grade): (sid, cid) is the primary key (composite). sid is a foreign key to Student. cid is a foreign key to Course. {sid, cid, grade} is a superkey but not a candidate key, because grade can be removed and (sid, cid) still identifies the row.
Example: Enroll(sid, cid, grade)-এ: (sid, cid) হলো primary key (composite)। sid হলো Student-এর foreign key। cid হলো Course-এর foreign key। {sid, cid, grade} একটা superkey, কিন্তু candidate key না — কারণ grade বাদ দিলেও (sid, cid) দিয়েই row চেনা যায়।

Integrity constraintsIntegrity constraints

  • Domain constraint: every value must come from the attribute's domain. Example: age cannot be 'abc'.
  • Key constraint: no two tuples can have the same value of a candidate key.
  • Entity integrity: primary key can never be NULL. Reason: we could not identify the row.
  • Referential integrity: a foreign key value must exist in the referenced table (or be NULL). Example: you cannot insert Enroll with sid = 999 if no Student has sid 999.
  • Domain constraint: প্রতিটা value অবশ্যই attribute-এর domain থেকে আসতে হবে। যেমন: age কখনো 'abc' হতে পারে না।
  • Key constraint: দুটো tuple-এ candidate key-এর একই value থাকতে পারে না।
  • Entity integrity: primary key কখনো NULL হতে পারে না। কারণ: তাহলে row-টা চেনা যেত না।
  • Referential integrity: foreign key-এর value অবশ্যই referenced table-এ থাকতে হবে (অথবা NULL হতে হবে)। যেমন: sid = 999 এর কোনো Student না থাকলে Enroll-এ sid = 999 insert করা যাবে না।
Exam tip: "Every candidate key is a superkey, but not every superkey is a candidate key." This one line answers many MCQs. Also: a relation can have many candidate keys but only ONE primary key.
Exam tip: "প্রতিটা candidate key-ই superkey, কিন্তু প্রতিটা superkey candidate key না।" এই এক লাইনেই অনেক MCQ-র উত্তর হয়ে যায়। আরও: এক relation-এ অনেক candidate key থাকতে পারে, কিন্তু primary key মাত্র একটা।

3. SQL3. SQL

SQL (Structured Query Language) is the language we use to create tables and work with data. Through this whole chapter we use one sample schema:

SQL (Structured Query Language) হলো table বানানো আর data নিয়ে কাজ করার language। পুরো chapter-এ আমরা একটাই sample schema ব্যবহার করব:

Student(sid, name, dept)
Course(cid, title, credit)
Enroll(sid, cid, grade)   -- sid → Student, cid → Course

Our sample data (used in every example below):

আমাদের sample data (নিচের সব example-এ এটাই ব্যবহার হবে):

Student
sidnamedept
101RahimCSE
102KarimCSE
103SaraEEE
104NadiaME
Course
cidtitlecredit
C1Database3
C2Networks3
C3Algorithms4
Enroll
sidcidgrade
101C1A
101C3B
102C1B
103C2A

SQL command classification (DDL, DML, DCL, TCL)SQL command classification (DDL, DML, DCL, TCL)

SQL commands fall into four families. The exam often gives you a list of commands and asks which family each one belongs to.

SQL command চারটা family-তে ভাগ হয়। Exam-এ প্রায়ই কিছু command দিয়ে জিজ্ঞেস করে, কোনটা কোন family-র।

FamilyFamily Full nameপুরো নাম What it doesকী করে CommandsCommands
DDL Data Definition Language Builds or changes the STRUCTURE (schema) of tablesTable-এর STRUCTURE (schema) বানায় বা বদলায় CREATE, ALTER, DROP, TRUNCATE
DML Data Manipulation Language Works with the DATA inside tablesTable-এর ভিতরের DATA নিয়ে কাজ করে SELECT, INSERT, UPDATE, DELETE
DCL Data Control Language Controls PERMISSIONS — who can do whatPERMISSION নিয়ন্ত্রণ করে — কে কী করতে পারবে GRANT, REVOKE
TCL Transaction Control Language Controls TRANSACTIONS — save or undo workTRANSACTION নিয়ন্ত্রণ করে — কাজ save বা undo করা COMMIT, ROLLBACK, SAVEPOINT
Real BUET question (asked October 2018): "Classify the following SQL commands into DDL, DML, DCL and TCL." Memorize this table. Trap to remember: TRUNCATE removes all rows but it is DDL (it resets the table structure-level storage and cannot use WHERE), while DELETE is DML. Also SELECT is counted as DML (some books call it DQL — say DML unless the option DQL exists).
Real BUET question (asked October 2018): "নিচের SQL command-গুলোকে DDL, DML, DCL আর TCL-এ ভাগ করুন।" এই table-টা মুখস্থ রাখুন। মনে রাখার trap: TRUNCATE সব row মুছে দিলেও এটা DDL (এটা table-এর storage reset করে, WHERE ব্যবহার করা যায় না), কিন্তু DELETE হলো DML। আর SELECT-কে DML ধরা হয় (কোনো কোনো বইয়ে DQL বলে — option-এ DQL না থাকলে DML বলুন)।

Creating and changing data (DDL + DML)Data বানানো আর বদলানো (DDL + DML)

CREATE TABLE makes a new table with column types and keys:

CREATE TABLE দিয়ে নতুন table বানানো হয়, column-এর type আর key সহ:

CREATE TABLE Student (
  sid   INT PRIMARY KEY,
  name  VARCHAR(50) NOT NULL,
  dept  VARCHAR(10)
);

CREATE TABLE Enroll (
  sid   INT,
  cid   VARCHAR(5),
  grade CHAR(1),
  PRIMARY KEY (sid, cid),
  FOREIGN KEY (sid) REFERENCES Student(sid),
  FOREIGN KEY (cid) REFERENCES Course(cid)
);

INSERT adds a row, UPDATE changes rows, DELETE removes rows:

INSERT নতুন row যোগ করে, UPDATE row বদলায়, DELETE row মুছে দেয়:

INSERT INTO Student (sid, name, dept) VALUES (105, 'Tanvir', 'CSE');

UPDATE Enroll SET grade = 'A' WHERE sid = 102 AND cid = 'C1';

DELETE FROM Enroll WHERE grade = 'F';
Careful: UPDATE or DELETE without a WHERE changes/removes EVERY row. A classic exam trap.
সাবধান: WHERE ছাড়া UPDATE বা DELETE দিলে সব row বদলে/মুছে যায়। এটা exam-এর একটা classic trap।

DDL with ALL constraint types — worked exampleসব constraint type সহ DDL — worked example

A written question often says: "Create the tables with proper constraints." There are five constraint types you must know:

  • PRIMARY KEY: unique + not null. Identifies each row.
  • FOREIGN KEY: value must exist in the referenced table. ON DELETE CASCADE means: if the parent row is deleted, the child rows are deleted too.
  • NOT NULL: the column can never be empty.
  • UNIQUE: no two rows can have the same value (but NULL may be allowed).
  • CHECK: every value must pass a condition, like salary > 0.

Written প্রশ্নে প্রায়ই বলে: "Proper constraint সহ table-গুলো create করুন।" পাঁচটা constraint type জানতেই হবে:

  • PRIMARY KEY: unique + not null। প্রতিটা row-কে চেনায়।
  • FOREIGN KEY: value-টা referenced table-এ থাকতেই হবে। ON DELETE CASCADE মানে: parent row delete হলে child row-গুলোও delete হয়ে যায়।
  • NOT NULL: column-টা কখনো খালি থাকতে পারবে না।
  • UNIQUE: দুটো row-তে একই value থাকতে পারবে না (তবে NULL allow হতে পারে)।
  • CHECK: প্রতিটা value-কে একটা condition pass করতে হবে, যেমন salary > 0
CREATE TABLE Department (
  dept_id   INT PRIMARY KEY,                -- PRIMARY KEY
  dept_name VARCHAR(50) NOT NULL UNIQUE     -- NOT NULL + UNIQUE
);

CREATE TABLE Employee (
  emp_id   INT PRIMARY KEY,                 -- PRIMARY KEY
  name     VARCHAR(50) NOT NULL,            -- NOT NULL
  email    VARCHAR(80) UNIQUE,              -- UNIQUE
  salary   DECIMAL(10,2) CHECK (salary > 0),-- CHECK
  dept_id  INT NOT NULL,
  FOREIGN KEY (dept_id)                     -- FOREIGN KEY
    REFERENCES Department(dept_id)
    ON DELETE CASCADE
);

What each line protects: you cannot insert two employees with the same emp_id (PRIMARY KEY), or a nameless employee (NOT NULL), or two employees with the same email (UNIQUE), or a salary of 0 or less (CHECK), or an employee in a department that does not exist (FOREIGN KEY). And if a department is deleted, all its employees go with it (ON DELETE CASCADE).

প্রতিটা line কী আটকায়: একই emp_id-এর দুইজন employee insert করা যাবে না (PRIMARY KEY), নাম-ছাড়া employee যাবে না (NOT NULL), একই email-এর দুইজন যাবে না (UNIQUE), salary 0 বা তার কম যাবে না (CHECK), আর যে department নেই সেখানে employee যাবে না (FOREIGN KEY)। আর কোনো department delete হলে তার সব employee-ও চলে যাবে (ON DELETE CASCADE)।

Real BUET question (asked October 2017): exactly this — write DDL for a departments/employees schema with PRIMARY KEY, FOREIGN KEY, NOT NULL, UNIQUE and CHECK constraints. Practice writing this block from memory. Order matters: create Department BEFORE Employee, because the foreign key must reference an existing table.
Real BUET question (asked October 2017): ঠিক এটাই — PRIMARY KEY, FOREIGN KEY, NOT NULL, UNIQUE আর CHECK constraint সহ departments/employees schema-র DDL লেখা। এই block-টা মুখস্থ লেখার practice করুন। Order গুরুত্বপূর্ণ: Employee-র আগে Department create করুন, কারণ foreign key-কে existing table-কে reference করতে হয়।

Conditional UPDATE with CASECASE দিয়ে conditional UPDATE

Sometimes one UPDATE must apply DIFFERENT changes to different rows. CASE ... WHEN ... THEN ... ELSE ... END does this: for each row, it picks the first matching WHEN branch.

Problem (exam style): table Product(pid, pname, cost). Increase cost by 20% if cost is below 100, by 10% if cost is 100 to 500, and by 5% otherwise — in ONE statement.

কখনো কখনো একটা UPDATE-কেই আলাদা আলাদা row-তে আলাদা change করতে হয়। CASE ... WHEN ... THEN ... ELSE ... END সেটাই করে: প্রতিটা row-এর জন্য প্রথম যে WHEN branch মেলে, সেটা বেছে নেয়।

Problem (exam style): table Product(pid, pname, cost)। cost 100-এর কম হলে 20% বাড়ান, 100 থেকে 500 হলে 10%, নাহলে 5% — একটা মাত্র statement-এ।

UPDATE Product
SET cost = CASE
             WHEN cost < 100  THEN cost * 1.20
             WHEN cost <= 500 THEN cost * 1.10
             ELSE                  cost * 1.05
           END;
pid cost beforeআগের cost branch usedযে branch লাগল cost afterপরের cost
P180< 100 → ×1.2096.00
P2200<= 500 → ×1.10220.00
P3900ELSE → ×1.05945.00
Real BUET question (asked April 2019): an UPDATE with CASE for conditional cost adjustment, just like this. Two things to remember: (1) branches are checked top to bottom, so cost <= 500 only sees rows that already failed cost < 100; (2) there is no WHERE here on purpose — every row gets exactly one branch. Always end the expression with END.
Real BUET question (asked April 2019): conditional cost adjustment-এর জন্য CASE সহ UPDATE — ঠিক এইরকম। দুটো জিনিস মনে রাখুন: (১) branch উপর থেকে নিচে check হয়, তাই cost <= 500 শুধু সেই row-গুলো দেখে যেগুলো আগেই cost < 100-এ fail করেছে; (২) এখানে ইচ্ছে করেই WHERE নেই — প্রতিটা row ঠিক একটা branch পায়। Expression সবসময় END দিয়ে শেষ করুন।

SELECT: reading dataSELECT: data পড়া

Get CSE students, sorted by name, only first 2 rows:

CSE-র student-দের বের করা, name দিয়ে sort করে, শুধু প্রথম ২টা row:

SELECT sid, name
FROM Student
WHERE dept = 'CSE'
ORDER BY name ASC
LIMIT 2;
ResultResult
sidname
102Karim
101Rahim

Order of writing: SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT. WHERE can use AND, OR, NOT, BETWEEN, IN, LIKE (pattern: 'R%' = starts with R), and comparisons like credit >= 3 or credit < 4.

লেখার order: SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMITWHERE-এ ব্যবহার করা যায়: AND, OR, NOT, BETWEEN, IN, LIKE (pattern: 'R%' মানে R দিয়ে শুরু), আর comparison যেমন credit >= 3 বা credit < 4

Aggregate functions, GROUP BY, HAVINGAggregate function, GROUP BY, HAVING

Aggregates work on a group of rows: COUNT, SUM, AVG, MAX, MIN. GROUP BY makes the groups. HAVING filters the groups after grouping.

Count students in each department, but keep only departments with more than 1 student:

Aggregate function এক group row-এর উপর কাজ করে: COUNT, SUM, AVG, MAX, MINGROUP BY group বানায়। HAVING group বানানোর পরে group filter করে।

প্রতি department-এ student গোনা, কিন্তু শুধু সেই department রাখা যেখানে ১ জনের বেশি student আছে:

SELECT dept, COUNT(*) AS total
FROM Student
GROUP BY dept
HAVING COUNT(*) > 1;
ResultResult
depttotal
CSE2
Exam tip — WHERE vs HAVING: WHERE filters rows before grouping; HAVING filters groups after grouping. You cannot write an aggregate like COUNT(*) inside WHERE — only inside HAVING (or SELECT). BUET-style questions love this difference.
Exam tip — WHERE vs HAVING: WHERE group বানানোর আগে row filter করে; HAVING group বানানোর পরে group filter করে। WHERE-এর ভিতরে COUNT(*)-এর মতো aggregate লেখা যায় না — শুধু HAVING (বা SELECT)-এ লেখা যায়। BUET-style প্রশ্নে এই পার্থক্যটা খুব আসে।

JoinsJoins

A join combines rows from two tables using a matching condition. We will join Student and Enroll on sid. Note: Nadia (104) has no enrollment, and there is no enroll row without a student.

  • INNER JOIN: only matching rows from both sides.
  • LEFT JOIN: all rows of the left table; NULLs where the right side has no match.
  • RIGHT JOIN: all rows of the right table; NULLs on the left side.
  • FULL JOIN: all rows of both; NULLs where there is no match.

Join একটা matching condition দিয়ে দুই table-এর row জোড়া দেয়। আমরা Student আর Enroll-কে sid দিয়ে join করব। লক্ষ্য করুন: Nadia (104)-এর কোনো enrollment নেই, আর student ছাড়া কোনো enroll row নেই।

  • INNER JOIN: দুই পাশেই match করা row-গুলো শুধু।
  • LEFT JOIN: বাম table-এর সব row; ডান পাশে match না থাকলে NULL।
  • RIGHT JOIN: ডান table-এর সব row; বাম পাশে NULL।
  • FULL JOIN: দুই পাশের সব row; match না থাকলে NULL।
SELECT S.sid, S.name, E.cid, E.grade
FROM Student S
INNER JOIN Enroll E ON S.sid = E.sid;
INNER JOIN resultINNER JOIN result
sidnamecidgrade
101RahimC1A
101RahimC3B
102KarimC1B
103SaraC2A
SELECT S.sid, S.name, E.cid, E.grade
FROM Student S
LEFT JOIN Enroll E ON S.sid = E.sid;
LEFT JOIN result (Nadia stays, with NULLs)LEFT JOIN result (Nadia থাকে, NULL সহ)
sidnamecidgrade
101RahimC1A
101RahimC3B
102KarimC1B
103SaraC2A
104NadiaNULLNULL

RIGHT JOIN here gives the same rows as INNER JOIN, because every Enroll row already has a matching student. FULL JOIN here gives the same as LEFT JOIN. In general: FULL = LEFT ∪ RIGHT.

এখানে RIGHT JOIN INNER JOIN-এর মতোই result দেবে, কারণ প্রতিটা Enroll row-এর matching student আছে। FULL JOIN এখানে LEFT JOIN-এর সমান। সাধারণভাবে: FULL = LEFT ∪ RIGHT।

Example: find students who took NO course (classic!).
SELECT S.name
FROM Student S
LEFT JOIN Enroll E ON S.sid = E.sid
WHERE E.sid IS NULL;

Result: Nadia. The LEFT JOIN keeps her with NULLs, and IS NULL catches exactly those rows.

Example: যে student কোনো course নেয়নি, তাদের বের করা (classic!)।
SELECT S.name
FROM Student S
LEFT JOIN Enroll E ON S.sid = E.sid
WHERE E.sid IS NULL;

Result: Nadia। LEFT JOIN তাকে NULL সহ রেখে দেয়, আর IS NULL ঠিক সেই row-গুলোই ধরে।

Subqueries and nested queriesSubquery আর nested query

A subquery is a query inside another query. Three common patterns:

1) IN — set membership. Names of students enrolled in the Database course:

Subquery হলো এক query-র ভিতরে আরেকটা query। ৩টা common pattern:

1) IN — set membership। Database course-এ enroll করা student-দের name:

SELECT name FROM Student
WHERE sid IN (SELECT sid FROM Enroll
              WHERE cid = (SELECT cid FROM Course
                           WHERE title = 'Database'));
ResultResult
name
Rahim
Karim

2) Scalar subquery — one value. Courses with credit higher than the average credit:

2) Scalar subquery — একটা মাত্র value। Average credit-এর চেয়ে বেশি credit-এর course:

SELECT title FROM Course
WHERE credit > (SELECT AVG(credit) FROM Course);
-- AVG = (3+3+4)/3 = 3.33 → result: Algorithms

3) EXISTS — correlated subquery. The inner query uses a value from the outer row. Students who enrolled in at least one course:

3) EXISTS — correlated subquery। ভিতরের query বাইরের row-এর value ব্যবহার করে। যে student-রা কমপক্ষে একটা course-এ enroll করেছে:

SELECT name FROM Student S
WHERE EXISTS (SELECT * FROM Enroll E
              WHERE E.sid = S.sid);
-- Result: Rahim, Karim, Sara (not Nadia)
Exam tip: "Students who took no course" can be written 3 ways: LEFT JOIN + IS NULL, NOT IN subquery, or NOT EXISTS. Being able to write all three is a common written-question ask.
Exam tip: "যে student কোনো course নেয়নি" — এটা ৩ ভাবে লেখা যায়: LEFT JOIN + IS NULL, NOT IN subquery, অথবা NOT EXISTS। তিনটাই লিখতে পারা written প্রশ্নে প্রায়ই লাগে।

TriggersTriggers

A trigger is a stored program attached to a table that the database runs automatically when a certain event happens — an INSERT, UPDATE, or DELETE. You never call a trigger yourself; it fires by itself. A trigger has three parts:

  • Event: what fires it — INSERT, UPDATE, or DELETE on a table.
  • Timing: BEFORE the change (good for checking/fixing values) or AFTER the change (good for logging and updating other tables).
  • Action: the SQL that runs, often with an optional WHEN condition.

Row level vs statement level: a row-level trigger (FOR EACH ROW) runs once for EVERY row the statement changes — if one UPDATE touches 50 rows, it fires 50 times. A statement-level trigger runs once per statement, no matter how many rows changed. Inside a row-level trigger, OLD holds the row's values before the change and NEW holds them after.

Trigger হলো table-এর সাথে লাগানো একটা stored program, যা কোনো event ঘটলে database নিজে থেকেই চালায় — event মানে INSERT, UPDATE বা DELETE। Trigger আপনি নিজে কখনো call করেন না; এটা নিজে নিজেই fire হয়। Trigger-এর তিনটা অংশ:

  • Event: কী এটাকে fire করে — table-এর উপর INSERT, UPDATE বা DELETE।
  • Timing: change-এর BEFORE (value check/ঠিক করার জন্য ভালো) বা AFTER (log রাখা আর অন্য table update-এর জন্য ভালো)।
  • Action: যে SQL চলবে, সাথে ঐচ্ছিক একটা WHEN condition থাকতে পারে।

Row level vs statement level: row-level trigger (FOR EACH ROW) statement যে row-গুলো বদলায়, তার প্রতিটার জন্য একবার চলে — এক UPDATE ৫০টা row ছুঁলে ৫০ বার fire হয়। Statement-level trigger প্রতি statement-এ একবারই চলে, যত row-ই বদলাক। Row-level trigger-এর ভিতরে OLD মানে change-এর আগের value, আর NEW মানে পরের value।

Example: audit log on salary update. Whenever an employee's salary changes, save the old and new value into SalaryLog(emp_id, old_salary, new_salary, changed_on):
Example: salary update-এর audit log। কোনো employee-র salary বদলালেই পুরনো আর নতুন value SalaryLog(emp_id, old_salary, new_salary, changed_on)-এ save হবে:
CREATE TRIGGER salary_audit
AFTER UPDATE ON Employee
FOR EACH ROW
WHEN (NEW.salary <> OLD.salary)
BEGIN
  INSERT INTO SalaryLog(emp_id, old_salary, new_salary, changed_on)
  VALUES (OLD.emp_id, OLD.salary, NEW.salary, CURRENT_DATE);
END;

Read it line by line: fire after an UPDATE on Employee, once for each row, but only when the salary actually changed — then insert one log row using OLD and NEW.

Line ধরে পড়ুন: Employee-তে UPDATE-এর after fire হবে, প্রতিটা row-এর জন্য একবার, কিন্তু শুধু যখন salary সত্যিই বদলেছে — তখন OLD আর NEW ব্যবহার করে একটা log row insert হবে।

When to use a trigger: audit logs (who changed what), keeping a computed/summary table in sync, enforcing rules a normal constraint cannot express (like "salary can never decrease"). When to avoid: heavy business logic — triggers are hidden, so code becomes hard to debug; a trigger firing another trigger can cause chains (even infinite loops); and row-level triggers on bulk updates can be slow.

Trigger কখন ব্যবহার করবেন: audit log (কে কী বদলাল), কোনো computed/summary table sync রাখা, আর এমন rule enforce করা যা সাধারণ constraint দিয়ে লেখা যায় না (যেমন "salary কখনো কমতে পারবে না")। কখন এড়াবেন: ভারী business logic-এ — trigger লুকানো থাকে, তাই code debug করা কঠিন হয়ে যায়; এক trigger আরেক trigger fire করলে chain (এমনকি infinite loop) হতে পারে; আর bulk update-এ row-level trigger slow হতে পারে।

Real BUET question (asked April 2019): "What is a trigger? Explain with an example." A full-marks answer needs: the definition (auto-fired stored program on INSERT/UPDATE/DELETE), BEFORE vs AFTER, row vs statement level, OLD/NEW, and one small CREATE TRIGGER like the one above.
Real BUET question (asked April 2019): "Trigger কী? Example সহ ব্যাখ্যা করুন।" Full marks পেতে লাগবে: definition (INSERT/UPDATE/DELETE-এ auto-fire হওয়া stored program), BEFORE vs AFTER, row vs statement level, OLD/NEW, আর উপরেরটার মতো একটা ছোট CREATE TRIGGER।

4. Relational Algebra4. Relational Algebra

Relational algebra is the math behind SQL. Each operator takes one or two relations and gives a new relation. Same schema as before: Student, Course, Enroll.

Relational algebra হলো SQL-এর পেছনের math। প্রতিটা operator এক বা দুটো relation নেয়, আর নতুন একটা relation দেয়। Schema আগের মতোই: Student, Course, Enroll।

OperatorOperator Nameনাম What it doesকী করে ExampleExample
\( \sigma \)Selection Picks ROWS that satisfy a conditionCondition মেলা ROW গুলো বেছে নেয় \( \sigma_{dept='CSE'}(Student) \)
\( \pi \)Projection Picks COLUMNS (removes duplicates)COLUMN বেছে নেয় (duplicate বাদ দেয়) \( \pi_{name}(Student) \)
\( \times \)Cartesian product Every row of A paired with every row of BA-র প্রতিটা row-এর সাথে B-র প্রতিটা row জোড়া হয় \( Student \times Enroll \)
\( \bowtie \)Natural join Product + match on common columns + remove duplicate columnProduct + common column-এ match + duplicate column বাদ \( Student \bowtie Enroll \)
\( \cup \)Union Rows in A or B (schemas must match)A বা B-তে থাকা row (schema একই হতে হবে) \( A \cup B \)
\( \cap \)Intersection Rows in both A and BA আর B দুটোতেই থাকা row \( A \cap B \)
\( - \)Difference Rows in A but not in BA-তে আছে কিন্তু B-তে নেই এমন row \( A - B \)
\( \rho \)Rename Renames a relation or its attributesRelation বা attribute-এর নাম বদলায় \( \rho_{S}(Student) \)
Example: step by step.

\( \sigma_{dept='CSE'}(Student) \) gives rows (101, Rahim, CSE) and (102, Karim, CSE).

\( \pi_{name}(\sigma_{dept='CSE'}(Student)) \) then keeps only the name column: {Rahim, Karim}.

\( Student \bowtie Enroll \) matches on the common column sid. It gives 4 rows — the same rows as the INNER JOIN result table in the SQL section.

Example: ধাপে ধাপে।

\( \sigma_{dept='CSE'}(Student) \) দেয় (101, Rahim, CSE) আর (102, Karim, CSE) row দুটো।

\( \pi_{name}(\sigma_{dept='CSE'}(Student)) \) এরপর শুধু name column রাখে: {Rahim, Karim}।

\( Student \bowtie Enroll \) common column sid-এ match করে। এটা ৪টা row দেয় — SQL section-এর INNER JOIN result table-এর row-গুলোই।

Relational algebra ↔ SQL, side by sideRelational algebra ↔ SQL, পাশাপাশি

Taskকাজ Relational algebra SQL
CSE studentsCSE-র student \( \sigma_{dept='CSE'}(Student) \) SELECT * FROM Student WHERE dept='CSE'
Only namesশুধু name \( \pi_{name}(Student) \) SELECT DISTINCT name FROM Student
Names of CSE studentsCSE student-দের name \( \pi_{name}(\sigma_{dept='CSE'}(Student)) \) SELECT DISTINCT name FROM Student WHERE dept='CSE'
Students with their gradesStudent আর তাদের grade \( \pi_{name,grade}(Student \bowtie Enroll) \) SELECT DISTINCT name, grade FROM Student NATURAL JOIN Enroll
Students with no courseCourse না নেওয়া student \( \pi_{sid}(Student) - \pi_{sid}(Enroll) \) SELECT sid FROM Student EXCEPT SELECT sid FROM Enroll
Names of students in course C1C1 course-এর student-দের name \( \pi_{name}(Student \bowtie \sigma_{cid='C1'}(Enroll)) \) SELECT DISTINCT name FROM Student NATURAL JOIN Enroll WHERE cid='C1'
Exam tip: Two differences to remember. (1) \( \sigma \) picks rows, \( \pi \) picks columns — do not swap them. (2) Relational algebra works on sets, so \( \pi \) removes duplicates automatically; SQL keeps duplicates unless you write DISTINCT. Also: \( A \bowtie B = \sigma_{match}(A \times B) \) with duplicate columns removed — join is just product + selection.
Exam tip: দুটো পার্থক্য মনে রাখুন। (১) \( \sigma \) row বাছে, \( \pi \) column বাছে — উল্টে ফেলবেন না। (২) Relational algebra set নিয়ে কাজ করে, তাই \( \pi \) নিজে থেকেই duplicate বাদ দেয়; SQL-এ DISTINCT না লিখলে duplicate থেকে যায়। আরও: \( A \bowtie B = \sigma_{match}(A \times B) \), duplicate column বাদ দিয়ে — মানে join আসলে product + selection।

5. Normalization5. Normalization

Normalization means breaking a badly designed table into smaller, cleaner tables. The goal is to remove redundancy (same data stored many times) and anomalies:

  • Update anomaly: change one fact, must update many rows.
  • Insert anomaly: cannot add a fact without another unrelated fact.
  • Delete anomaly: deleting a row silently deletes a different fact too.

Normalization মানে খারাপভাবে design করা table-কে ছোট ছোট পরিষ্কার table-এ ভাঙা। উদ্দেশ্য: redundancy (একই data বারবার রাখা) আর anomaly দূর করা:

  • Update anomaly: একটা fact বদলাতে অনেক row update করতে হয়।
  • Insert anomaly: আরেকটা অপ্রাসঙ্গিক fact ছাড়া নতুন fact add করা যায় না।
  • Delete anomaly: একটা row delete করলে চুপচাপ অন্য একটা fact-ও মুছে যায়।

Functional dependency (FD)Functional dependency (FD)

A functional dependency \( X \to Y \) means: if two rows agree on X, they must also agree on Y. In words, "X determines Y".

Example: sid → name (one sid always has one name). sid, cid → grade (one student in one course has one grade).

Functional dependency \( X \to Y \) মানে: দুটো row-তে X একই হলে Y-ও একই হতে হবে। সহজ কথায়, "X, Y-কে determine করে"।

যেমন: sid → name (এক sid-এর সবসময় এক name)। sid, cid → grade (এক student-এর এক course-এ এক grade)।

Armstrong's axioms (briefly)Armstrong's axioms (সংক্ষেপে)

  • Reflexivity: if \( Y \subseteq X \), then \( X \to Y \). (AB → A is always true.)
  • Augmentation: if \( X \to Y \), then \( XZ \to YZ \). (Add the same thing to both sides.)
  • Transitivity: if \( X \to Y \) and \( Y \to Z \), then \( X \to Z \).

Derived rules: Union (X→Y and X→Z give X→YZ), Decomposition (X→YZ gives X→Y and X→Z), Pseudo-transitivity.

  • Reflexivity: \( Y \subseteq X \) হলে \( X \to Y \)। (AB → A সবসময় সত্য।)
  • Augmentation: \( X \to Y \) হলে \( XZ \to YZ \)। (দুই পাশে একই জিনিস যোগ করুন।)
  • Transitivity: \( X \to Y \) আর \( Y \to Z \) হলে \( X \to Z \)।

Derived rule: Union (X→Y আর X→Z থেকে X→YZ), Decomposition (X→YZ থেকে X→Y আর X→Z), Pseudo-transitivity

Attribute closure — worked exampleAttribute closure — worked example

The closure of X, written \( X^+ \), is the set of ALL attributes X can determine. To find it: start with X, and keep adding the right side of any FD whose left side is already inside.

X-এর closure, লেখা হয় \( X^+ \) — X যত attribute determine করতে পারে, তাদের set। বের করার নিয়ম: X দিয়ে শুরু করুন, আর যে FD-র বাম পাশ ইতিমধ্যে ভিতরে আছে, তার ডান পাশ যোগ করতে থাকুন।

Example: R(A, B, C, D), FDs: A → B, B → C, CD → A. Find \( A^+ \).
  1. Start: \( A^+ = \{A\} \)
  2. A → B fires: \( A^+ = \{A, B\} \)
  3. B → C fires: \( A^+ = \{A, B, C\} \)
  4. CD → A needs D, which we do not have. Stop.

\( A^+ = \{A, B, C\} \). D is missing, so A is NOT a key. Check \( (AD)^+ \): start {A,D} → add B → add C → now CD inside → all of {A,B,C,D}. So AD is a candidate key. Similarly BD and CD are candidate keys too (check them the same way). Note: D appears on no right-hand side, so D must be part of EVERY key.

Example: R(A, B, C, D), FD: A → B, B → C, CD → A। \( A^+ \) বের করুন।
  1. শুরু: \( A^+ = \{A\} \)
  2. A → B কাজ করে: \( A^+ = \{A, B\} \)
  3. B → C কাজ করে: \( A^+ = \{A, B, C\} \)
  4. CD → A-এর জন্য D লাগবে, যা আমাদের নেই। থামুন।

\( A^+ = \{A, B, C\} \)। D নেই, তাই A key নয়। এবার \( (AD)^+ \) দেখুন: শুরু {A,D} → B যোগ → C যোগ → এখন CD ভিতরে → পুরো {A,B,C,D}। তাই AD একটা candidate key। একইভাবে BD আর CD-ও candidate key (একই নিয়মে check করুন)। লক্ষ্য করুন: D কোনো FD-র ডান পাশে নেই, তাই D প্রতিটা key-তে থাকতেই হবে।

Key-finding shortcut: an attribute that never appears on any right-hand side of an FD must be in every candidate key. Start your key search from those attributes. BUET written questions often ask "find all candidate keys" — this trick saves time.
Key খোঁজার shortcut: যে attribute কোনো FD-র ডান পাশে কখনো আসে না, সেটা প্রতিটা candidate key-তে থাকবেই। সেই attribute থেকে খোঁজা শুরু করুন। BUET-এর written প্রশ্নে প্রায়ই আসে "সব candidate key বের করুন" — এই trick-এ সময় বাঁচে।

1NF — atomic values1NF — atomic value

Rule: every cell holds ONE atomic value. No lists, no repeating groups.

Violation:

নিয়ম: প্রতিটা cell-এ শুধু একটা atomic value। কোনো list বা repeating group না।

ভাঙার example:

sidnamephone
101Rahim017xxx, 018xxx
102Karim019xxx

Fix: one phone per row — or better, a separate table StudentPhone(sid, phone).

ঠিক করা: প্রতি row-তে একটা phone — অথবা আরও ভালো, আলাদা table StudentPhone(sid, phone)

2NF — no partial dependency2NF — কোনো partial dependency না

Rule: 1NF + no non-prime attribute depends on a part of a composite key. (A "non-prime" attribute is one that is not in any candidate key.)

Violation: Enroll2(sid, cid, grade, sname), key = (sid, cid). Here sid → sname: sname depends on only PART of the key.

নিয়ম: 1NF + কোনো non-prime attribute composite key-এর অংশের উপর নির্ভর করবে না। ("Non-prime" attribute মানে যেটা কোনো candidate key-তে নেই।)

ভাঙার example: Enroll2(sid, cid, grade, sname), key = (sid, cid)। এখানে sid → sname: sname key-এর শুধু একটা অংশের উপর নির্ভর করছে।

sidcidgradesname
101C1ARahim
101C3BRahim
102C1BKarim

"Rahim" is stored twice — redundancy. Fix: split into Student(sid, sname) and Enroll(sid, cid, grade).

"Rahim" দুইবার store হয়েছে — redundancy। ঠিক করা: ভেঙে ফেলুন: Student(sid, sname) আর Enroll(sid, cid, grade)

3NF — no transitive dependency3NF — কোনো transitive dependency না

Rule: 2NF + no non-prime attribute depends on another non-prime attribute (non-key → non-key is banned).

Violation: Student2(sid, dept, dept_head), key = sid. FDs: sid → dept, dept → dept_head. So sid → dept_head is transitive.

নিয়ম: 2NF + কোনো non-prime attribute অন্য non-prime attribute-এর উপর নির্ভর করবে না (non-key → non-key নিষেধ)।

ভাঙার example: Student2(sid, dept, dept_head), key = sid। FD: sid → dept, dept → dept_head। তাই sid → dept_head হলো transitive

siddeptdept_head
101CSEDr. Alam
102CSEDr. Alam
103EEEDr. Karim

"Dr. Alam" repeats. If CSE gets a new head, we must update many rows (update anomaly). Fix: Student(sid, dept) + Dept(dept, dept_head).

"Dr. Alam" বারবার আসছে। CSE-তে নতুন head এলে অনেক row update করতে হবে (update anomaly)। ঠিক করা: Student(sid, dept) + Dept(dept, dept_head)

BCNF — every determinant is a keyBCNF — প্রতিটা determinant-ই key

Rule: for EVERY non-trivial FD \( X \to Y \), X must be a superkey. Stricter than 3NF.

Violation: Teach(sid, course, teacher). Rules: each teacher teaches only one course; a course can have many teachers; a student takes a course with one teacher. FDs: teacher → course and (sid, course) → teacher. Candidate keys: (sid, course) and (sid, teacher). The FD teacher → course breaks BCNF because teacher is not a superkey. But it does NOT break 3NF, because course is a prime attribute (part of a key).

Fix (BCNF decomposition): split on the bad FD: TeacherCourse(teacher, course) and StudentTeacher(sid, teacher).

নিয়ম: প্রতিটা non-trivial FD \( X \to Y \)-এর জন্য X-কে superkey হতে হবে। এটা 3NF-এর চেয়ে কড়া।

ভাঙার example: Teach(sid, course, teacher)। নিয়ম: প্রতিটা teacher একটাই course পড়ান; এক course-এ অনেক teacher থাকতে পারেন; এক student এক course এক teacher-এর কাছে পড়ে। FD: teacher → course আর (sid, course) → teacher। Candidate key: (sid, course) আর (sid, teacher)। teacher → course FD-টা BCNF ভাঙে, কারণ teacher superkey না। কিন্তু এটা 3NF ভাঙে না, কারণ course একটা prime attribute (key-এর অংশ)।

ঠিক করা (BCNF decomposition): খারাপ FD ধরে ভাঙুন: TeacherCourse(teacher, course) আর StudentTeacher(sid, teacher)

Exam tip — 3NF vs BCNF: 3NF allows an FD X → Y when Y is a prime attribute even if X is not a superkey. BCNF never allows it. So: BCNF ⊆ 3NF (every BCNF relation is in 3NF, not the reverse). Also remember: BCNF decomposition is always lossless but may NOT preserve all dependencies; 3NF can always keep both.
Exam tip — 3NF vs BCNF: Y যদি prime attribute হয়, তাহলে X superkey না হলেও 3NF সেই FD X → Y-কে allow করে। BCNF কখনোই করে না। তাই: BCNF ⊆ 3NF (প্রতিটা BCNF relation 3NF-তেও আছে, উল্টোটা না)। আরও মনে রাখুন: BCNF decomposition সবসময় lossless, কিন্তু সব dependency preserve না-ও হতে পারে; 3NF-এ দুটোই সবসময় রাখা যায়।
Exam tip — lossless join: a decomposition of R into R1, R2 is lossless if the common attributes (R1 ∩ R2) form a key of R1 or of R2. Example: splitting Student2 into (sid, dept) and (dept, dept_head) is lossless because the common attribute dept is the key of (dept, dept_head). Lossless means: joining the pieces back gives exactly the original table — no extra (spurious) rows.
Exam tip — lossless join: R-কে R1, R2-তে ভাঙা lossless হবে যদি common attribute (R1 ∩ R2) R1 বা R2-এর key হয়। যেমন: Student2-কে (sid, dept) আর (dept, dept_head)-এ ভাঙা lossless, কারণ common attribute dept হলো (dept, dept_head)-এর key। Lossless মানে: টুকরোগুলো join করলে ঠিক আগের table-টাই পাওয়া যায় — কোনো বাড়তি (spurious) row আসে না।

Full decomposition — worked example (BUET favorite)Full decomposition — worked example (BUET favorite)

Problem: R(A, B, C, D, E) with FDs: AB → C, C → D, D → E. Find the candidate key, the normal form, and decompose to BCNF.

Step 1 — key. A and B never appear on a right side → both must be in every key. \( (AB)^+ \): {A,B} → add C (AB→C) → add D (C→D) → add E (D→E) = {A,B,C,D,E}. So AB is the only candidate key. Non-prime: C, D, E.

Step 2 — normal form. 1NF: yes. 2NF: C depends on the FULL key AB (no partial FD on A alone or B alone) — yes. 3NF: C → D is non-prime → non-prime (transitive) — 3NF fails. So R is in 2NF only.

Step 3 — decompose. Break on D → E: R1(D, E) and R'(A, B, C, D). Break R' on C → D: R2(C, D) and R3(A, B, C).

Result: R1(D, E), R2(C, D), R3(A, B, C). Check each: in R1, D is key; in R2, C is key; in R3, AB is key and AB → C is fine. All FDs now have a superkey on the left → BCNF. Each split shared attributes that are a key of one piece (D for R1, C for R2) → lossless.

Problem: R(A, B, C, D, E), FD: AB → C, C → D, D → E। Candidate key, normal form বের করুন আর BCNF-এ decompose করুন।

Step 1 — key। A আর B কোনো FD-র ডান পাশে নেই → দুটোই প্রতিটা key-তে থাকবে। \( (AB)^+ \): {A,B} → C যোগ (AB→C) → D যোগ (C→D) → E যোগ (D→E) = {A,B,C,D,E}। তাই AB-ই একমাত্র candidate key। Non-prime: C, D, E।

Step 2 — normal form। 1NF: হ্যাঁ। 2NF: C পুরো key AB-এর উপর নির্ভর করে (শুধু A বা শুধু B-এর উপর partial FD নেই) — হ্যাঁ। 3NF: C → D হলো non-prime → non-prime (transitive) — 3NF fail। তাই R শুধু 2NF-এ আছে।

Step 3 — decompose। D → E ধরে ভাঙুন: R1(D, E) আর R'(A, B, C, D)। এবার R'-কে C → D ধরে ভাঙুন: R2(C, D) আর R3(A, B, C)।

Result: R1(D, E), R2(C, D), R3(A, B, C)। প্রতিটা check করুন: R1-এ D key; R2-তে C key; R3-এ AB key আর AB → C ঠিক আছে। সব FD-র বাম পাশে এখন superkey → BCNF। প্রতিটা ভাগে common attribute এক টুকরোর key (R1-এর জন্য D, R2-এর জন্য C) → lossless।

Normal formNormal form Rule in one lineএক লাইনে নিয়ম Killsযা দূর করে
1NFAtomic values onlyশুধু atomic valueRepeating groupsRepeating group
2NFNo partial dependency on part of keyKey-এর অংশের উপর partial dependency নাPartial FDPartial FD
3NFNo non-key → non-key dependencyNon-key → non-key dependency নাTransitive FDTransitive FD
BCNFEvery determinant is a superkeyপ্রতিটা determinant একটা superkeyAll FD anomaliesসব FD anomaly

6. Transactions & Concurrency6. Transactions & Concurrency

A transaction is a group of operations that must run as one unit. Classic example: transfer 500 taka from account A to account B — subtract from A, add to B. Both must happen, or neither.

Transaction হলো একগুচ্ছ operation যা একসাথে এক unit হিসেবে চলতে হবে। Classic example: account A থেকে B-তে ৫০০ টাকা transfer — A থেকে বাদ, B-তে যোগ। দুটোই হবে, নাহলে কোনোটাই না।

ACID propertiesACID properties

  • Atomicity: all or nothing. If the system crashes after subtracting from A but before adding to B, the subtraction is rolled back.
  • Consistency: the database moves from one valid state to another. Total money before = total money after.
  • Isolation: running transactions do not see each other's half-done work. Two people booking the last seat cannot both get it.
  • Durability: once committed, the change survives crashes and power loss (written to permanent storage / log).
  • Atomicity: সব হবে, নাহলে কিছুই না। A থেকে বাদ দেওয়ার পর, B-তে যোগ করার আগে system crash করলে বাদ দেওয়াটাও rollback হয়ে যায়।
  • Consistency: database এক valid state থেকে আরেক valid state-এ যায়। আগের মোট টাকা = পরের মোট টাকা।
  • Isolation: চলমান transaction-রা একে অপরের অর্ধেক-করা কাজ দেখে না। দুইজন শেষ seat book করতে গেলে দুজনেই পাবে না।
  • Durability: একবার commit হলে crash বা কারেন্ট চলে গেলেও change টিকে থাকে (permanent storage / log-এ লেখা হয়)।

Schedules and serializabilitySchedule আর serializability

A schedule is the order in which operations of several transactions run, mixed together. A serial schedule runs one full transaction, then the next — always safe, but slow. A schedule is serializable if its final effect equals SOME serial schedule. That is the safety goal: run in parallel, but behave like serial.

Schedule হলো কয়েকটা transaction-এর operation মিলেমিশে যে order-এ চলে। Serial schedule-এ একটা transaction পুরো শেষ হয়, তারপর পরেরটা — সবসময় safe, কিন্তু slow। কোনো schedule serializable যদি তার final ফলাফল কোনো একটা serial schedule-এর সমান হয়। এটাই লক্ষ্য: parallel-এ চলবে, কিন্তু আচরণ হবে serial-এর মতো।

Conflict serializability and the precedence graphConflict serializability আর precedence graph

Two operations conflict if: they are from different transactions, touch the same data item, and at least one is a WRITE. So conflicts are read–write, write–read, write–write. Read–read never conflicts.

Precedence graph test: make a node for each transaction. If an operation of \( T_i \) conflicts with a LATER operation of \( T_j \), draw edge \( T_i \to T_j \). The schedule is conflict serializable if and only if the graph has no cycle. A topological order of the graph gives the equivalent serial order.

দুটো operation conflict করে যদি: তারা আলাদা transaction-এর হয়, একই data item ছোঁয়, আর অন্তত একটা WRITE হয়। তাই conflict হলো read–write, write–read, write–write। Read–read কখনো conflict না।

Precedence graph test: প্রতিটা transaction-এর জন্য একটা node আঁকুন। \( T_i \)-এর কোনো operation যদি \( T_j \)-এর পরের কোনো operation-এর সাথে conflict করে, তাহলে edge \( T_i \to T_j \) আঁকুন। Graph-এ cycle না থাকলে, আর শুধু তখনই, schedule-টা conflict serializable। Graph-এর topological order-ই equivalent serial order।

Example: Schedule S: R1(X), W1(X), R2(X), W2(X), R2(Y), W2(Y), R1(Y), W1(Y).

Conflicts on X: W1(X) before R2(X)/W2(X) → edge T1 → T2.

Conflicts on Y: W2(Y) before R1(Y)/W1(Y) → edge T2 → T1.

The graph has cycle T1 → T2 → T1 → NOT conflict serializable.

But schedule S2: R1(X), W1(X), R2(X), W2(X), R1(Y), W1(Y), R2(Y), W2(Y) gives edges T1 → T2 (on X) and T1 → T2 (on Y) — no cycle → serializable, equivalent to serial order T1, T2.

Example: Schedule S: R1(X), W1(X), R2(X), W2(X), R2(Y), W2(Y), R1(Y), W1(Y)।

X-এ conflict: W1(X) আগে, R2(X)/W2(X) পরে → edge T1 → T2।

Y-এ conflict: W2(Y) আগে, R1(Y)/W1(Y) পরে → edge T2 → T1।

Graph-এ cycle T1 → T2 → T1 → conflict serializable না

কিন্তু schedule S2: R1(X), W1(X), R2(X), W2(X), R1(Y), W1(Y), R2(Y), W2(Y) দিলে edge হয় T1 → T2 (X-এ) আর T1 → T2 (Y-এ) — কোনো cycle নেই → serializable, serial order T1, T2-এর সমান।

Schedule S: cycle → NOT serializable T1 T2 X Y Schedule S2: no cycle → serializable T1 T2 X Y
Precedence graphs: a cycle means not conflict serializable; no cycle means serializable. Precedence graph: cycle থাকলে conflict serializable না; cycle না থাকলে serializable।

Locking and 2PLLocking আর 2PL

Locks control access to data items. A shared lock (S) lets many transactions read. An exclusive lock (X) lets one transaction write — nobody else can read or write it.

Two-Phase Locking (2PL): each transaction has a growing phase (only takes locks) and then a shrinking phase (only releases locks). Once it releases any lock, it can never take a new one. 2PL guarantees conflict serializability. Strict 2PL holds all exclusive locks until commit — this also avoids cascading rollback.

Lock দিয়ে data item-এ access নিয়ন্ত্রণ করা হয়। Shared lock (S)-এ অনেক transaction পড়তে পারে। Exclusive lock (X)-এ একটা transaction লিখতে পারে — অন্য কেউ পড়তেও পারে না, লিখতেও পারে না।

Two-Phase Locking (2PL): প্রতিটা transaction-এর একটা growing phase (শুধু lock নেয়) আর একটা shrinking phase (শুধু lock ছাড়ে) থাকে। একবার কোনো lock ছাড়লে আর নতুন lock নিতে পারে না। 2PL conflict serializability guarantee করেStrict 2PL সব exclusive lock commit পর্যন্ত ধরে রাখে — এতে cascading rollback-ও এড়ানো যায়।

Lost Update AnomalyLost Update Anomaly

The lost update is the classic problem that locking exists to solve. Two transactions read the same value, both compute a new value from it, and both write. The second write silently overwrites the first — so one update is lost, as if it never happened.

Lost update হলো সেই classic সমস্যা যেটা সমাধান করতেই locking এসেছে। দুইটা transaction একই value পড়ে, দুজনেই সেটা থেকে নতুন value হিসাব করে, আর দুজনেই write করে। দ্বিতীয় write-টা চুপচাপ প্রথমটাকে overwrite করে দেয় — মানে একটা update হারিয়ে যায়, যেন কখনো হয়ইনি।

Example: Account balance A = 100. T1 wants to add 50 (deposit). T2 wants to subtract 30 (withdrawal). The correct final balance is 100 + 50 − 30 = 120. But watch this interleaved schedule:
Example: Account balance A = 100। T1 চায় 50 যোগ করতে (deposit)। T2 চায় 30 বিয়োগ করতে (withdrawal)। সঠিক final balance হওয়া উচিত 100 + 50 − 30 = 120। কিন্তু এই interleaved schedule-টা দেখুন:
StepStep T1 T2 What happensকী ঘটে
1read(A) → 100T1 sees 100T1 দেখে 100
2read(A) → 100T2 also sees 100 (too early!)T2-ও দেখে 100 (বেশি আগে!)
3A = 100 + 50T1 computes 150T1 হিসাব করে 150
4write(A) = 150Balance is now 150Balance এখন 150
5A = 100 − 30T2 computes 70 from the OLD valueT2 পুরনো value থেকে হিসাব করে 70
6write(A) = 70Overwrites 150 — T1's update is LOST150 overwrite হয়ে যায় — T1-এর update LOST

Final balance = 70, but the correct answer is 120. T1's +50 vanished. This is the lost update anomaly.

Final balance = 70, কিন্তু সঠিক উত্তর 120। T1-এর +50 উধাও। এটাই lost update anomaly।

How strict 2PL prevents it: T1 takes an exclusive (X) lock on A before reading and updating it, and holds it until commit. When T2 asks for its own X lock on A at step 2, the request blocks — T2 must wait. Only after T1 commits (balance = 150) does T2 get the lock, read the fresh value 150, and write 150 − 30 = 120. Correct answer, no lost update. The schedule becomes equivalent to running T1 then T2 one after another (serializable).

Strict 2PL কীভাবে এটা আটকায়: T1 পড়া আর update-এর আগে A-এর উপর exclusive (X) lock নেয়, আর commit পর্যন্ত ধরে রাখে। Step 2-এ T2 যখন A-এর উপর নিজের X lock চায়, request-টা block হয় — T2-কে অপেক্ষা করতে হয়। T1 commit করার পরেই (balance = 150) T2 lock পায়, fresh value 150 পড়ে, আর 150 − 30 = 120 write করে। সঠিক উত্তর, কোনো lost update নেই। Schedule-টা তখন T1-এর পরে T2 চালানোর সমান হয়ে যায় (serializable)।

Exam note: The lost update anomaly is a classic exam favorite (practice-bank staple). Typical question: show the interleaved schedule, ask for the wrong final value, then ask how 2PL fixes it. Remember the pattern: both read old value → both write → second write kills the first.
Exam note: Lost update anomaly একটা classic exam favorite (practice-bank staple)। সাধারণ প্রশ্ন: interleaved schedule দেখিয়ে ভুল final value জিজ্ঞেস করে, তারপর 2PL কীভাবে ঠিক করে তা চায়। Pattern-টা মনে রাখুন: দুজনেই পুরনো value পড়ে → দুজনেই write করে → দ্বিতীয় write প্রথমটাকে মুছে দেয়

DeadlockDeadlock

Deadlock: T1 holds a lock on X and waits for Y; T2 holds Y and waits for X. Both wait forever. Detection: build a wait-for graph; a cycle = deadlock. The system then aborts one transaction (the victim). Prevention schemes: wait-die (older waits, younger dies) and wound-wait (older wounds younger, younger waits), or simply lock items in a fixed global order.

Deadlock: T1, X-এর lock ধরে Y-এর জন্য অপেক্ষা করছে; T2, Y ধরে X-এর জন্য অপেক্ষা করছে। দুজনেই চিরকাল অপেক্ষা করবে। Detection: wait-for graph বানান; cycle থাকলেই deadlock। তখন system একটা transaction-কে abort করে (victim)। Prevention: wait-die (পুরনোটা অপেক্ষা করে, নতুনটা মরে) আর wound-wait (পুরনোটা নতুনটাকে wound করে, নতুনটা অপেক্ষা করে), অথবা সবাই একই fixed order-এ lock নেয়।

Isolation levels (brief)Isolation level (সংক্ষেপে)

LevelLevel Dirty readDirty read Non-repeatable readNon-repeatable read Phantom readPhantom read
READ UNCOMMITTEDpossibleহতে পারেpossibleহতে পারেpossibleহতে পারে
READ COMMITTEDnoনাpossibleহতে পারেpossibleহতে পারে
REPEATABLE READnoনাnoনাpossibleহতে পারে
SERIALIZABLEnoনাnoনাnoনা

Quick meanings: dirty read = reading uncommitted data of another transaction; non-repeatable read = same row gives a different value when read twice; phantom = re-running a query finds new rows that appeared in between.

সংক্ষেপে অর্থ: dirty read = অন্য transaction-এর uncommitted data পড়া; non-repeatable read = একই row দুইবার পড়লে আলাদা value; phantom = query আবার চালালে মাঝখানে আসা নতুন row ধরা পড়ে।

Exam tip: Memorize the conflict pairs (RW, WR, WW — never RR), the precedence-graph cycle rule, and that 2PL guarantees serializability but can still cause deadlock. These three facts cover most MCQs on this topic.
Exam tip: Conflict pair-গুলো মুখস্থ রাখুন (RW, WR, WW — RR কখনো না), precedence graph-এর cycle rule, আর 2PL serializability guarantee করলেও deadlock ঘটাতে পারে — এই তিনটা fact-এই এই topic-এর বেশিরভাগ MCQ কভার হয়ে যায়।

7. Indexing7. Indexing

Without an index, finding a row means scanning the whole table — like reading a whole book to find one word. An index is like the book's index page: a sorted structure of (search key, pointer to row). It makes lookup fast, but takes extra space and slows down inserts/updates a little (the index must be updated too).

Index ছাড়া একটা row খুঁজতে পুরো table scan করতে হয় — যেন একটা শব্দ খুঁজতে পুরো বই পড়া। Index হলো বইয়ের index page-এর মতো: (search key, row-এর pointer)-এর একটা sorted structure। এতে খোঁজা fast হয়, কিন্তু বাড়তি জায়গা লাগে আর insert/update একটু slow হয় (index-ও update করতে হয়)।

Dense vs sparse indexDense vs sparse index

  • Dense index: one index entry for EVERY search-key value in the file. Faster lookup, more space.
  • Sparse index: index entries for only SOME values (usually one per block/page). To find a key, go to the largest index entry ≤ key, then scan that block. Needs the data file to be sorted on the key. Less space, slightly slower.
  • Dense index: file-এর প্রতিটা search-key value-র জন্য একটা করে index entry। খোঁজা faster, জায়গা বেশি লাগে।
  • Sparse index: শুধু কিছু value-র জন্য entry (সাধারণত প্রতি block/page-এ একটা)। খুঁজতে হলে key-এর চেয়ে ছোট বা সমান সবচেয়ে বড় entry-তে যান, তারপর সেই block scan করুন। এর জন্য data file-টা key দিয়ে sorted থাকতে হয়। জায়গা কম লাগে, একটু slower।
Dense index Sparse index 101 102 103 104 101 Rahim 102 Karim 103 Sara 104 Nadia one entry per record 101 103 101 Rahim 102 Karim 103 Sara 104 Nadia one entry per block
Dense index: an entry for every record. Sparse index: an entry for every block of the sorted file. Dense index: প্রতিটা record-এর জন্য entry। Sparse index: sorted file-এর প্রতিটা block-এর জন্য entry।

Primary vs secondary indexPrimary vs secondary index

  • Primary (clustering) index: built on the key the data file is SORTED by. At most one per table. Can be sparse.
  • Secondary index: built on any other attribute; the file is not sorted by it. Must be dense (every value needs its own pointer). A table can have many secondary indexes.
  • Primary (clustering) index: যে key দিয়ে data file sorted, তার উপর বানানো। প্রতি table-এ সর্বোচ্চ একটা। Sparse হতে পারে।
  • Secondary index: অন্য যেকোনো attribute-এর উপর; file সেটা দিয়ে sorted না। এটা dense হতেই হবে (প্রতিটা value-র নিজের pointer লাগে)। এক table-এ অনেক secondary index থাকতে পারে।

B-tree and B+ treeB-tree আর B+ tree

A B-tree is a balanced search tree where each node holds many keys (high fan-out) and all leaves are at the same depth. Data pointers can sit at every node. A node of order \( n \) has at most \( n \) children and at least \( \lceil n/2 \rceil \) (root excepted); this keeps the tree balanced and the height around \( O(\log n) \) with a very large base.

A B+ tree is the database version of a B-tree:

  • All records/data pointers live ONLY in the leaf nodes. Internal nodes hold only keys for routing.
  • Leaves are linked left-to-right like a linked list.
  • Every search goes root → leaf, so every lookup costs the same (height of tree, usually 3–4 disk reads even for millions of rows).

Why databases prefer B+ tree: (1) internal nodes hold only keys, so more keys fit per disk block → bigger fan-out → shorter tree → fewer disk reads; (2) the linked leaves make range queries (WHERE sid BETWEEN 100 AND 200) a simple walk along the leaf chain; (3) uniform search cost.

B-tree হলো একটা balanced search tree, যেখানে প্রতিটা node-এ অনেক key থাকে (high fan-out) আর সব leaf একই depth-এ। Data pointer যেকোনো node-এ থাকতে পারে। Order \( n \)-এর একটা node-এ সর্বোচ্চ \( n \)টা child আর কমপক্ষে \( \lceil n/2 \rceil \)টা child থাকে (root বাদে); এতে tree balanced থাকে আর height হয় প্রায় \( O(\log n) \), খুব বড় base-এ।

B+ tree হলো B-tree-র database version:

  • সব record/data pointer শুধু leaf node-এ থাকে। Internal node-এ শুধু routing-এর key থাকে।
  • Leaf-গুলো linked list-এর মতো বাম থেকে ডানে linked
  • প্রতিটা search root → leaf যায়, তাই প্রতিটা lookup-এর খরচ সমান (tree-র height, লাখ লাখ row হলেও সাধারণত ৩–৪টা disk read)।

Database কেন B+ tree পছন্দ করে: (১) internal node-এ শুধু key থাকে, তাই এক disk block-এ বেশি key ধরে → বড় fan-out → খাটো tree → কম disk read; (২) linked leaf-এর জন্য range query (WHERE sid BETWEEN 100 AND 200) মানে শুধু leaf chain ধরে হাঁটা; (৩) সব search-এর খরচ সমান।

B+ tree structure with internal routing nodes and linked leaf nodes containing data pointers
A B+ tree: internal nodes route the search; all data pointers sit in the linked leaf level. (Image: Wikimedia Commons) একটা B+ tree: internal node-গুলো search-কে পথ দেখায়; সব data pointer থাকে linked leaf level-এ। (ছবি: Wikimedia Commons)
Example: why the tree stays short. Suppose one node (one disk block) can hold 100 keys. Height 1 reaches 100 keys, height 2 reaches about \( 100 \times 100 = 10{,}000 \), height 3 reaches about \( 10^6 \). So a million-row table needs only about 3 levels — about 3 disk reads per lookup instead of scanning a million rows.
Example: tree কেন খাটো থাকে। ধরুন এক node (এক disk block)-এ ১০০টা key ধরে। Height 1-এ ১০০টা key, height 2-এ প্রায় \( 100 \times 100 = 10{,}000 \), height 3-এ প্রায় \( 10^6 \)। তাই ১০ লাখ row-এর table-এ মাত্র ৩টা level লাগে — ১০ লাখ row scan না করে প্রতি lookup-এ মাত্র ৩টা disk read।

B+ tree construction — step-by-step worked exampleB+ tree construction — ধাপে ধাপে worked example

Problem: build a B+ tree of order \( n = 5 \) by inserting 12, 14, 19, 30, 21, 22, 13, 34, 18, 23, 38, 40 in this order. Order 5 means: a node has at most 5 pointers, so at most 4 keys.

Split rules (memorize): when a 5th key would enter a leaf, split it — the first \( \lceil 5/2 \rceil = 3 \) keys stay in the left leaf, the last 2 go to a new right leaf, and the FIRST key of the right leaf is copied up into the parent (copied, because in a B+ tree every key must also stay at the leaf level). If an internal node overflows, its middle key moves up instead (not needed in this problem).

Problem: order \( n = 5 \)-এর একটা B+ tree বানান, এই order-এ insert করে: 12, 14, 19, 30, 21, 22, 13, 34, 18, 23, 38, 40। Order 5 মানে: এক node-এ সর্বোচ্চ ৫টা pointer, তাই সর্বোচ্চ ৪টা key

Split-এর নিয়ম (মুখস্থ রাখুন): leaf-এ ৫ নম্বর key ঢুকতে গেলে split করুন — প্রথম \( \lceil 5/2 \rceil = 3 \)টা key বাম leaf-এ থাকে, শেষ ২টা নতুন ডান leaf-এ যায়, আর ডান leaf-এর FIRST key parent-এ copy হয়ে উপরে যায় (copy, কারণ B+ tree-তে প্রতিটা key leaf level-এও থাকতেই হবে)। Internal node overflow করলে তার মাঝের key copy না হয়ে উপরে চলে যায় (এই problem-এ লাগবে না)।

InsertInsert What happensকী ঘটে Tree afterপরের tree
12, 14, 19, 30 All fit in one leaf (4 keys = full but legal)সব এক leaf-এ ধরে (৪ key = full কিন্তু বৈধ) [12 14 19 30]
21 SPLIT 1: sorted set {12,14,19,21,30} overflows → left [12 14 19], right [21 30], copy 21 up → new rootSPLIT 1: sorted {12,14,19,21,30} overflow → বাম [12 14 19], ডান [21 30], 21 copy হয়ে উপরে → নতুন root [21] / [12 14 19] [21 30]
22 22 ≥ 21 → right leaf, fits22 ≥ 21 → ডান leaf-এ, ধরে যায় [21] / [12 14 19] [21 22 30]
13 13 < 21 → left leaf, fits13 < 21 → বাম leaf-এ, ধরে যায় [21] / [12 13 14 19] [21 22 30]
34 34 ≥ 21 → right leaf, fits (now full)34 ≥ 21 → ডান leaf-এ, ধরে যায় (এখন full) [21] / [12 13 14 19] [21 22 30 34]
18 SPLIT 2: {12,13,14,18,19} overflows → [12 13 14] and [18 19], copy 18 upSPLIT 2: {12,13,14,18,19} overflow → [12 13 14] আর [18 19], 18 copy হয়ে উপরে [18 21] / [12 13 14] [18 19] [21 22 30 34]
23 SPLIT 3: {21,22,23,30,34} overflows → [21 22 23] and [30 34], copy 30 upSPLIT 3: {21,22,23,30,34} overflow → [21 22 23] আর [30 34], 30 copy হয়ে উপরে [18 21 30] / [12 13 14] [18 19] [21 22 23] [30 34]
38 38 ≥ 30 → last leaf, fits38 ≥ 30 → শেষ leaf-এ, ধরে যায় [18 21 30] / [12 13 14] [18 19] [21 22 23] [30 34 38]
40 40 ≥ 30 → last leaf, fits (4 keys = full but legal). DONE.40 ≥ 30 → শেষ leaf-এ, ধরে যায় (৪ key = full কিন্তু বৈধ)। শেষ। [18 21 30] / [12 13 14] [18 19] [21 22 23] [30 34 38 40]
18 21 30 12 13 14 18 19 21 22 23 30 34 38 40 linked leaf chain (for range queries)
Final B+ tree (order 5): root [18 | 21 | 30] routing to 4 linked leaves. All 12 keys sit in the leaves; check: 3 + 2 + 3 + 4 = 12. Final B+ tree (order 5): root [18 | 21 | 30] থেকে ৪টা linked leaf। ১২টা key-ই leaf-এ আছে; হিসাব: 3 + 2 + 3 + 4 = 12।
Real BUET question (asked April 2017): exactly these 12 numbers with \( n = 5 \). In the exam, redraw the whole tree after EVERY split (3 splits here) — that is where the marks are. Quick self-checks: every key that went up (18, 21, 30) must still appear in a leaf; leaves must be sorted left to right; every non-root leaf must have at least \( \lceil (n-1)/2 \rceil = 2 \) keys.
Real BUET question (asked April 2017): ঠিক এই ১২টা সংখ্যাই, \( n = 5 \) দিয়ে। Exam-এ প্রতিটা SPLIT-এর পরে পুরো tree আবার আঁকুন (এখানে ৩টা split) — নম্বর ওখানেই। Quick self-check: যে key উপরে গেছে (18, 21, 30) সেগুলো leaf-এও থাকতে হবে; leaf-গুলো বাম থেকে ডানে sorted হতে হবে; root ছাড়া প্রতিটা leaf-এ কমপক্ষে \( \lceil (n-1)/2 \rceil = 2 \)টা key থাকতে হবে।

Hash index (briefly)Hash index (সংক্ষেপে)

A hash index applies a hash function to the key: \( h(key) \) gives the bucket where the record sits. Equality search (WHERE sid = 101) is O(1) on average — even faster than a B+ tree. But hashing keeps NO order, so it is useless for range queries (BETWEEN, <, >) and sorting. Static hashing fixes the bucket count (overflow chains grow); extendible/dynamic hashing grows the directory as data grows.

Hash index key-এর উপর একটা hash function চালায়: \( h(key) \) বলে দেয় record কোন bucket-এ আছে। Equality search (WHERE sid = 101) গড়ে O(1) — B+ tree-র চেয়েও fast। কিন্তু hash-এ কোনো order থাকে না, তাই range query (BETWEEN, <, >) আর sorting-এ এটা অকেজো। Static hashing-এ bucket সংখ্যা fixed (overflow chain বাড়ে); extendible/dynamic hashing-এ data বাড়ার সাথে directory বাড়ে।

Exam tip: "Which index for range queries?" → B+ tree. "Which for pure equality?" → hash. "Where is data stored in a B+ tree?" → only in leaves, and leaves are linked. These three come up again and again.
Exam tip: "Range query-র জন্য কোন index?" → B+ tree। "শুধু equality-র জন্য?" → hash। "B+ tree-তে data কোথায় থাকে?" → শুধু leaf-এ, আর leaf-গুলো linked। এই তিনটা বারবার আসে।

RAID Levels (Real Exam Topic!)RAID Levels (Real Exam Topic!)

Indexes make search fast, but the data itself lives on disks — and disks fail. RAID (Redundant Array of Independent Disks) combines several disks into one logical unit to get more speed, more safety (redundancy), or both. Each "level" is a different trade-off between performance, capacity, and how many disk failures you can survive.

Two building ideas: striping = split data into blocks and spread them across disks (many disks work in parallel → fast). Mirroring = keep a full copy of the data on another disk (a disk dies → the copy survives). Parity = store an extra XOR block; if one disk dies, its data can be rebuilt from the others plus the parity.

Index search fast করে, কিন্তু data নিজে থাকে disk-এ — আর disk fail করে। RAID (Redundant Array of Independent Disks) কয়েকটা disk-কে একটা logical unit বানায়, যাতে বেশি speed, বেশি safety (redundancy), বা দুটোই পাওয়া যায়। প্রতিটা "level" হলো performance, capacity আর কয়টা disk failure সহ্য করা যায় — এই তিনের আলাদা trade-off।

দুইটা মূল idea: striping = data-কে block-এ ভেঙে অনেক disk-এ ছড়িয়ে দেওয়া (অনেক disk একসাথে কাজ করে → fast)। Mirroring = data-র পুরো একটা copy আরেক disk-এ রাখা (একটা disk মরলে copy বেঁচে থাকে)। Parity = একটা বাড়তি XOR block রাখা; একটা disk মরলে বাকিগুলো আর parity থেকে তার data আবার বানানো যায়।

LevelLevel TechniqueTechnique Min disksMin disk Usable capacityUsable capacity Failure toleranceFailure tolerance
RAID 0 striping onlyশুধু striping 2 100% (n) 0 disks — 1 failure loses ALL data0টা disk — ১টা fail মানে সব data শেষ
RAID 1 mirroringmirroring 2 50% (n/2) 1 disk (per mirror pair)১টা disk (প্রতি mirror pair-এ)
RAID 5 striping + distributed paritystriping + distributed parity 3 (n−1)/n exactly 1 diskঠিক ১টা disk
RAID 6 striping + double paritystriping + double parity 4 (n−2)/n any 2 disksযেকোনো ২টা disk
RAID 10 mirror pairs, then stripemirror pair, তারপর stripe 4 50% (n/2) 1 per pair (up to n/2 if in different pairs)প্রতি pair-এ ১টা (আলাদা pair হলে n/2 পর্যন্ত)
  • RAID 0 (striping): blocks A1, A2, A3, A4 go round-robin across disks. Reads and writes are fast (parallel), and you use 100% of the space. But there is no redundancy: if any one disk fails, the whole array's data is gone. Use only for speed, never for important data.
  • RAID 1 (mirroring): every block is written to two disks. Full redundancy — one disk of a pair can die and nothing is lost. Reads can be served by either disk (fast reads), but you only get 50% usable capacity.
  • RAID 5 (striping + distributed parity): data is striped, and for each stripe one parity block (XOR of the data blocks) is stored — rotated across all disks so no single disk is the "parity disk". Needs at least 3 disks. Survives exactly 1 disk failure: the missing block is rebuilt as XOR of the surviving blocks. Usable capacity = (n−1)/n. Writes are a bit slower (parity must be updated).
  • RAID 6: like RAID 5 but with two independent parity blocks per stripe — survives any 2 disk failures; capacity (n−2)/n; needs ≥ 4 disks.
  • RAID 10 (1+0): make mirror pairs first, then stripe across the pairs — the speed of RAID 0 with the safety of RAID 1, at 50% capacity; needs ≥ 4 disks.
  • RAID 0 (striping): block A1, A2, A3, A4 পালা করে disk-গুলোতে যায়। Read আর write fast (parallel), আর space-এর 100% ব্যবহার হয়। কিন্তু কোনো redundancy নেই: যেকোনো একটা disk fail করলেই পুরো array-র data শেষ। শুধু speed-এর জন্য, দরকারি data-র জন্য কখনো না।
  • RAID 1 (mirroring): প্রতিটা block দুইটা disk-এ লেখা হয়। Full redundancy — pair-এর একটা disk মরলেও কিছু হারায় না। Read যেকোনো disk থেকে হতে পারে (fast read), কিন্তু usable capacity মাত্র 50%
  • RAID 5 (striping + distributed parity): data stripe হয়, আর প্রতি stripe-এর জন্য একটা parity block (data block-গুলোর XOR) রাখা হয় — সেটা ঘুরে ঘুরে সব disk-এ যায়, তাই আলাদা কোনো "parity disk" নেই। কমপক্ষে ৩টা disk লাগে। ঠিক ১টা disk failure সহ্য করে: হারানো block বেঁচে থাকা block-গুলোর XOR দিয়ে আবার বানানো যায়। Usable capacity = (n−1)/n। Write একটু slow (parity-ও update করতে হয়)।
  • RAID 6: RAID 5-এর মতো, কিন্তু প্রতি stripe-এ দুইটা আলাদা parity block — যেকোনো ২টা disk failure সহ্য করে; capacity (n−2)/n; ≥ ৪টা disk লাগে।
  • RAID 10 (1+0): আগে mirror pair বানায়, তারপর pair-গুলোর উপর stripe করে — RAID 0-এর speed আর RAID 1-এর safety একসাথে, 50% capacity-তে; ≥ ৪টা disk লাগে।
RAID 0 — striping (fast, no safety) Disk 1 A1 A3 Disk 2 A2 A4 1 disk fails → ALL lost RAID 1 — mirroring (safe, 50% space) Disk 1 A1 A2 Disk 2 A1 (copy) A2 (copy) 1 disk fails → copy survives RAID 5 — striping + distributed parity (P = XOR) Disk 1 A1 B1 Disk 2 A2 P(B) Disk 3 P(A) B2 parity rotates; any 1 disk can fail
RAID 0 stripes blocks for speed, RAID 1 mirrors them for safety, RAID 5 adds a rotating parity block so one failed disk can be rebuilt. RAID 0 speed-এর জন্য block stripe করে, RAID 1 safety-র জন্য mirror করে, RAID 5 ঘুরন্ত parity block রাখে যাতে একটা fail করা disk আবার বানানো যায়।
Example (capacity math): You have 4 disks of 1 TB each (total raw = 4 TB).
  • RAID 0: everything is data → usable = 4 TB. Tolerates 0 failures.
  • RAID 10: 2 mirror pairs, striped → usable = 4/2 = 2 TB. Tolerates 1 failure per pair.
  • RAID 5: one disk's worth goes to parity → usable = (4−1) × 1 = 3 TB. Tolerates exactly 1 failure.
Quick formulas: RAID 0 = n·d, RAID 1/10 = (n/2)·d, RAID 5 = (n−1)·d, RAID 6 = (n−2)·d, where n = number of disks and d = size of one disk.
Example (capacity math): আপনার কাছে ১ TB-এর ৪টা disk আছে (মোট raw = 4 TB)।
  • RAID 0: সবটাই data → usable = 4 TB। ০টা failure সহ্য করে।
  • RAID 10: ২টা mirror pair, তার উপর stripe → usable = 4/2 = 2 TB। প্রতি pair-এ ১টা failure সহ্য করে।
  • RAID 5: এক disk-এর সমান জায়গা parity-তে যায় → usable = (4−1) × 1 = 3 TB। ঠিক ১টা failure সহ্য করে।
Quick formula: RAID 0 = n·d, RAID 1/10 = (n/2)·d, RAID 5 = (n−1)·d, RAID 6 = (n−2)·d, যেখানে n = disk সংখ্যা আর d = একটা disk-এর size।
Exam note: RAID was asked in the 2021 BUET online exam. Remember the one-liners: RAID 0 = performance only (no redundancy), RAID 1 = mirroring (50% capacity), RAID 5 = striping + distributed parity (survives 1 failure, needs ≥ 3 disks, capacity (n−1)/n). MCQs love "which level has no redundancy?" (RAID 0) and "usable capacity of n disks in RAID 5?" ((n−1)·d).
Exam note: RAID এসেছিল 2021 BUET online exam-এ। One-liner-গুলো মনে রাখুন: RAID 0 = শুধু performance (কোনো redundancy নেই), RAID 1 = mirroring (50% capacity), RAID 5 = striping + distributed parity (১টা failure সহ্য করে, ≥ ৩টা disk লাগে, capacity (n−1)/n)। MCQ-তে খুব আসে: "কোন level-এ redundancy নেই?" (RAID 0) আর "RAID 5-এ n disk-এর usable capacity?" ((n−1)·d)।

Practice Questions (Admission Style)Practice Questions (Admission Style)

Q1. Which property of a transaction says "all operations happen, or none happen"?
  • (a) Consistency
  • (b) Atomicity
  • (c) Isolation
  • (d) Durability
Q1. Transaction-এর কোন property বলে "সব operation হবে, নাহলে কোনোটাই না"?
  • (a) Consistency
  • (b) Atomicity
  • (c) Isolation
  • (d) Durability
Show Answerউত্তর দেখুন
Answer: (b) — Atomicity means the transaction is one indivisible unit: either everything commits or everything rolls back. Consistency is about valid states, isolation about not seeing others' half work, durability about surviving crashes after commit.
Answer: (b) — Atomicity মানে transaction একটা অবিভাজ্য unit: হয় পুরোটা commit হবে, নাহলে পুরোটা rollback। Consistency হলো valid state নিয়ে, isolation হলো অন্যের অর্ধেক কাজ না দেখা, আর durability হলো commit-এর পর crash-এও টিকে থাকা।
Q2. An M:N relationship in an ER diagram becomes what in the relational model?
  • (a) A foreign key on one side
  • (b) A new table with both primary keys
  • (c) Merging the two entity tables
  • (d) A derived attribute
Q2. ER diagram-এর একটা M:N relationship, relational model-এ কী হয়?
  • (a) এক পাশে একটা foreign key
  • (b) দুই primary key নিয়ে একটা নতুন table
  • (c) দুই entity table merge করা
  • (d) একটা derived attribute
Show Answerউত্তর দেখুন
Answer: (b) — M:N cannot be stored with a single foreign key, because each side matches many rows on the other side. So we create a junction table holding both keys (like Enroll(sid, cid, grade)) whose primary key is the pair. A foreign key on one side works only for 1:N.
Answer: (b) — M:N একটা মাত্র foreign key দিয়ে রাখা যায় না, কারণ প্রতিটা পাশ অন্য পাশের অনেক row-এর সাথে match করে। তাই দুই key নিয়ে একটা junction table বানাতে হয় (যেমন Enroll(sid, cid, grade)), যার primary key হলো জোড়াটা। এক পাশে foreign key শুধু 1:N-এর জন্য কাজ করে।
Q3. Which query is INVALID?
  • (a) SELECT dept FROM Student WHERE dept = 'CSE'
  • (b) SELECT dept, COUNT(*) FROM Student GROUP BY dept
  • (c) SELECT dept FROM Student WHERE COUNT(*) > 1 GROUP BY dept
  • (d) SELECT dept FROM Student GROUP BY dept HAVING COUNT(*) > 1
Q3. কোন query-টা INVALID?
  • (a) SELECT dept FROM Student WHERE dept = 'CSE'
  • (b) SELECT dept, COUNT(*) FROM Student GROUP BY dept
  • (c) SELECT dept FROM Student WHERE COUNT(*) > 1 GROUP BY dept
  • (d) SELECT dept FROM Student GROUP BY dept HAVING COUNT(*) > 1
Show Answerউত্তর দেখুন
Answer: (c) — Aggregate functions like COUNT(*) cannot appear inside WHERE, because WHERE filters rows BEFORE groups exist. Filtering on an aggregate must use HAVING, as option (d) correctly does.
Answer: (c)COUNT(*)-এর মতো aggregate function WHERE-এর ভিতরে বসতে পারে না, কারণ WHERE group তৈরি হওয়ার আগেই row filter করে। Aggregate দিয়ে filter করতে হলে HAVING লাগবে, যেমনটা (d)-তে ঠিকভাবে করা হয়েছে।
Q4. R(A, B, C) has candidate key {A}. Which statement is TRUE?
  • (a) {A, B} is also a candidate key
  • (b) {A, B} is a superkey but not a candidate key
  • (c) {B} must be a superkey
  • (d) R can have no other candidate key
Q4. R(A, B, C)-এর candidate key {A}। কোন কথাটা সত্য?
  • (a) {A, B}-ও একটা candidate key
  • (b) {A, B} একটা superkey, কিন্তু candidate key না
  • (c) {B} অবশ্যই একটা superkey
  • (d) R-এর আর কোনো candidate key থাকতে পারে না
Show Answerউত্তর দেখুন
Answer: (b) — Any superset of a key is a superkey, so {A, B} identifies rows. But a candidate key must be minimal, and {A, B} minus B still works — so it is not minimal, hence not a candidate key. (d) is false: another attribute (like a unique B) could form a second candidate key.
Answer: (b) — Key-এর যেকোনো superset-ই superkey, তাই {A, B} দিয়ে row চেনা যায়। কিন্তু candidate key-কে minimal হতে হয়, আর {A, B} থেকে B বাদ দিলেও কাজ চলে — তাই এটা minimal না, মানে candidate key না। (d) ভুল: অন্য attribute (যেমন unique B) দ্বিতীয় candidate key হতে পারত।
Q5. Using this chapter's data (Student has 4 rows: 101, 102, 103, 104; Enroll has rows for sid 101, 101, 102, 103), how many rows does this query return?
SELECT S.name FROM Student S
LEFT JOIN Enroll E ON S.sid = E.sid;
  • (a) 3
  • (b) 4
  • (c) 5
  • (d) 6
Q5. এই chapter-এর data দিয়ে (Student-এ ৪টা row: 101, 102, 103, 104; Enroll-এ sid 101, 101, 102, 103-এর row), এই query কয়টা row দেবে?
SELECT S.name FROM Student S
LEFT JOIN Enroll E ON S.sid = E.sid;
  • (a) 3
  • (b) 4
  • (c) 5
  • (d) 6
Show Answerউত্তর দেখুন
Answer: (c) — The inner-match part gives 4 rows (101 matches twice, 102 once, 103 once). LEFT JOIN also keeps the unmatched student 104 (Nadia) with NULLs. Total 4 + 1 = 5 rows.
Answer: (c) — Match করা অংশ দেয় ৪টা row (101 দুইবার match করে, 102 একবার, 103 একবার)। LEFT JOIN match-না-হওয়া student 104 (Nadia)-কেও NULL সহ রাখে। মোট 4 + 1 = ৫টা row।
Q6. In relational algebra, which operator picks COLUMNS and automatically removes duplicate rows?
  • (a) \( \sigma \) (selection)
  • (b) \( \pi \) (projection)
  • (c) \( \bowtie \) (natural join)
  • (d) \( \rho \) (rename)
Q6. Relational algebra-তে কোন operator COLUMN বেছে নেয় আর নিজে থেকেই duplicate row বাদ দেয়?
  • (a) \( \sigma \) (selection)
  • (b) \( \pi \) (projection)
  • (c) \( \bowtie \) (natural join)
  • (d) \( \rho \) (rename)
Show Answerউত্তর দেখুন
Answer: (b) — Projection \( \pi \) keeps chosen columns. Since relations are sets, identical remaining rows collapse into one — like SQL's SELECT DISTINCT. \( \sigma \) picks rows, not columns.
Answer: (b) — Projection \( \pi \) বাছাই করা column রাখে। Relation যেহেতু set, বাকি অংশে একই রকম row একটাতে মিশে যায় — SQL-এর SELECT DISTINCT-এর মতো। \( \sigma \) row বাছে, column না।
Q7. R(sid, cid, grade, sname) has key (sid, cid), and FD sid → sname holds. What is the highest normal form of R?
  • (a) 1NF
  • (b) 2NF
  • (c) 3NF
  • (d) BCNF
Q7. R(sid, cid, grade, sname)-এর key (sid, cid), আর FD sid → sname আছে। R সর্বোচ্চ কোন normal form-এ আছে?
  • (a) 1NF
  • (b) 2NF
  • (c) 3NF
  • (d) BCNF
Show Answerউত্তর দেখুন
Answer: (a) — sname is a non-prime attribute that depends on sid, which is only PART of the composite key (sid, cid). That is a partial dependency, which 2NF forbids. So R is in 1NF but not 2NF. Fix: split into (sid, sname) and (sid, cid, grade).
Answer: (a) — sname একটা non-prime attribute, যেটা composite key (sid, cid)-এর শুধু একটা অংশ sid-এর উপর নির্ভর করে। এটাই partial dependency, যা 2NF-এ নিষেধ। তাই R 1NF-এ আছে কিন্তু 2NF-এ নেই। ঠিক করতে: (sid, sname) আর (sid, cid, grade)-এ ভাঙুন।
Q8. Which statement about a B+ tree is FALSE?
  • (a) All leaves are at the same depth
  • (b) Data pointers are stored only in leaf nodes
  • (c) Leaf nodes are linked together
  • (d) It is faster than a hash index for equality search
Q8. B+ tree সম্পর্কে কোন কথাটা মিথ্যা?
  • (a) সব leaf একই depth-এ থাকে
  • (b) Data pointer শুধু leaf node-এ থাকে
  • (c) Leaf node-গুলো একসাথে linked
  • (d) Equality search-এ এটা hash index-এর চেয়ে faster
Show Answerউত্তর দেখুন
Answer: (d) — For a single equality lookup, a hash index is O(1) on average, while a B+ tree costs O(height) ≈ O(log n). The B+ tree wins for RANGE queries and sorted scans, not pure equality. (a), (b), (c) are exactly the defining properties of a B+ tree.
Answer: (d) — একটা মাত্র equality lookup-এ hash index গড়ে O(1), আর B+ tree-তে লাগে O(height) ≈ O(log n)। B+ tree জেতে RANGE query আর sorted scan-এ, pure equality-তে না। (a), (b), (c) হলো B+ tree-র মূল বৈশিষ্ট্যই।
Q9. (Written) Using Student(sid, name, dept), Course(cid, title, credit), Enroll(sid, cid, grade): write SQL to (i) list names of students enrolled in the course titled 'Database', and (ii) show each dept with its number of students, only for depts with at least 2 students.
Q9. (Written) Student(sid, name, dept), Course(cid, title, credit), Enroll(sid, cid, grade) ব্যবহার করে SQL লিখুন: (i) 'Database' title-এর course-এ enroll করা student-দের name, আর (ii) প্রতিটা dept আর তার student সংখ্যা, শুধু যেসব dept-এ কমপক্ষে ২ জন student আছে।
Show Answerউত্তর দেখুন
Answer:
-- (i) join all three tables
SELECT DISTINCT S.name
FROM Student S
JOIN Enroll E ON S.sid = E.sid
JOIN Course C ON E.cid = C.cid
WHERE C.title = 'Database';

-- (ii) aggregate + group filter
SELECT dept, COUNT(*) AS total
FROM Student
GROUP BY dept
HAVING COUNT(*) >= 2;

Reasoning: (i) needs a path Student → Enroll → Course to reach the title, joined on sid and cid. (ii) counts per group, and the "at least 2" condition is on the aggregate, so it must go in HAVING, not WHERE. With our sample data, (i) returns Rahim, Karim and (ii) returns (CSE, 2).

Answer:
-- (i) তিনটা table join
SELECT DISTINCT S.name
FROM Student S
JOIN Enroll E ON S.sid = E.sid
JOIN Course C ON E.cid = C.cid
WHERE C.title = 'Database';

-- (ii) aggregate + group filter
SELECT dept, COUNT(*) AS total
FROM Student
GROUP BY dept
HAVING COUNT(*) >= 2;

ব্যাখ্যা: (i)-এ title পেতে Student → Enroll → Course path লাগবে, sid আর cid দিয়ে join। (ii)-এ group ধরে গোনা হয়, আর "কমপক্ষে ২" condition-টা aggregate-এর উপর, তাই সেটা WHERE-এ না, HAVING-এ যাবে। আমাদের sample data-তে (i) দেয় Rahim, Karim আর (ii) দেয় (CSE, 2)।

Q10. (Written) Write a relational algebra expression for: "names of students who are enrolled in course C1", and a second expression for: "sids of students enrolled in NO course".
Q10. (Written) Relational algebra expression লিখুন: "C1 course-এ enroll করা student-দের name", আর দ্বিতীয়টা: "কোনো course-এ enroll না করা student-দের sid"।
Show Answerউত্তর দেখুন
Answer:

(i) \( \pi_{name}(Student \bowtie \sigma_{cid='C1'}(Enroll)) \)

First filter Enroll to only C1 rows (σ), then natural join with Student on sid to attach names, then project name (π). Filtering before joining is also the efficient order.

(ii) \( \pi_{sid}(Student) - \pi_{sid}(Enroll) \)

All student ids minus the ids that appear in Enroll leaves exactly the students who never enrolled. Set difference (−) is the natural tool for "NO / not any" questions.

Answer:

(i) \( \pi_{name}(Student \bowtie \sigma_{cid='C1'}(Enroll)) \)

আগে Enroll থেকে শুধু C1-এর row filter করুন (σ), তারপর sid দিয়ে Student-এর সাথে natural join করে name আনুন, শেষে name project করুন (π)। Join-এর আগে filter করাটাই efficient order।

(ii) \( \pi_{sid}(Student) - \pi_{sid}(Enroll) \)

সব student-এর id থেকে Enroll-এ থাকা id বাদ দিলে ঠিক তারাই থাকে যারা কখনো enroll করেনি। "কোনোটাই না / NO" ধরনের প্রশ্নে set difference (−)-ই আসল হাতিয়ার।

Q11. (Written) R(A, B, C, D) with FDs: AB → C, C → D, D → A. Compute \( (AB)^+ \) and find ALL candidate keys.
Q11. (Written) R(A, B, C, D), FD: AB → C, C → D, D → A। \( (AB)^+ \) বের করুন আর সব candidate key খুঁজুন।
Show Answerউত্তর দেখুন
Answer:

\( (AB)^+ \): start {A,B} → AB→C adds C → C→D adds D → {A,B,C,D}. So AB is a key.

B never appears on any right side, so B is in EVERY candidate key. Test B alone: \( B^+ = \{B\} \) — not a key. Test pairs containing B:

  • \( (AB)^+ = ABCD \) ✓
  • \( (CB)^+ \): {B,C} → C→D adds D → D→A adds A → ABCD ✓
  • \( (DB)^+ \): {B,D} → D→A adds A → AB→C adds C → ABCD ✓

Candidate keys: AB, BC, BD. All are minimal (B alone fails, and A, C, D alone add nothing without B).

Answer:

\( (AB)^+ \): শুরু {A,B} → AB→C থেকে C → C→D থেকে D → {A,B,C,D}। তাই AB একটা key।

B কোনো FD-র ডান পাশে নেই, তাই B প্রতিটা candidate key-তে থাকবে। শুধু B দেখুন: \( B^+ = \{B\} \) — key না। এবার B-সহ জোড়াগুলো:

  • \( (AB)^+ = ABCD \) ✓
  • \( (CB)^+ \): {B,C} → C→D থেকে D → D→A থেকে A → ABCD ✓
  • \( (DB)^+ \): {B,D} → D→A থেকে A → AB→C থেকে C → ABCD ✓

Candidate key: AB, BC, BD। সবগুলোই minimal (শুধু B fail করে, আর B ছাড়া শুধু A, C বা D কিছুই দেয় না)।

Q12. (Written) Table: Order(order_id, customer_id, customer_name, product_id, product_name, qty), key = (order_id, product_id). FDs: order_id → customer_id, customer_id → customer_name, product_id → product_name, (order_id, product_id) → qty. Normalize to 3NF, naming each violation you fix.
Q12. (Written) Table: Order(order_id, customer_id, customer_name, product_id, product_name, qty), key = (order_id, product_id)। FD: order_id → customer_id, customer_id → customer_name, product_id → product_name, (order_id, product_id) → qty। 3NF-এ normalize করুন, কোন violation ঠিক করছেন তার নাম বলে।
Show Answerউত্তর দেখুন
Answer:

2NF step (remove partial dependencies): customer_id and customer_name depend on order_id alone; product_name depends on product_id alone — both are parts of the key. Split:

  • OrderInfo(order_id, customer_id, customer_name)
  • Product(product_id, product_name)
  • OrderLine(order_id, product_id, qty) — qty depends on the full key, so it stays.

3NF step (remove transitive dependency): in OrderInfo, order_id → customer_id → customer_name is non-key → non-key. Split again:

  • Orders(order_id, customer_id)
  • Customer(customer_id, customer_name)

Final 3NF schema: Orders(order_id, customer_id), Customer(customer_id, customer_name), Product(product_id, product_name), OrderLine(order_id, product_id, qty). Every FD now has a key on its left side, so this is even BCNF.

Answer:

2NF step (partial dependency দূর): customer_id আর customer_name শুধু order_id-এর উপর নির্ভর করে; product_name শুধু product_id-এর উপর — দুটোই key-এর অংশ। ভাঙুন:

  • OrderInfo(order_id, customer_id, customer_name)
  • Product(product_id, product_name)
  • OrderLine(order_id, product_id, qty) — qty পুরো key-এর উপর নির্ভর করে, তাই এখানেই থাকবে।

3NF step (transitive dependency দূর): OrderInfo-তে order_id → customer_id → customer_name হলো non-key → non-key। আবার ভাঙুন:

  • Orders(order_id, customer_id)
  • Customer(customer_id, customer_name)

Final 3NF schema: Orders(order_id, customer_id), Customer(customer_id, customer_name), Product(product_id, product_name), OrderLine(order_id, product_id, qty)। এখন প্রতিটা FD-র বাম পাশে key আছে, তাই এটা BCNF-ও বটে।

Q13. (Written) R(A, B, C) with FDs: AB → C and C → B. (i) Find all candidate keys. (ii) Is R in 3NF? In BCNF? (iii) Why is this a problem case?
Q13. (Written) R(A, B, C), FD: AB → C আর C → B। (i) সব candidate key বের করুন। (ii) R কি 3NF-এ আছে? BCNF-এ? (iii) এটা কেন একটা ঝামেলার case?
Show Answerউত্তর দেখুন
Answer:

(i) A appears on no right side → A is in every key. \( A^+ = \{A\} \), so A alone fails. \( (AB)^+ = \{A,B,C\} \) ✓ and \( (AC)^+ \): C→B adds B → \{A,B,C\} ✓. Candidate keys: AB and AC. So every attribute is prime.

(ii) 3NF: yes. The FD C → B has a non-superkey left side, but B is a prime attribute, and 3NF allows key → anything OR anything → prime. BCNF: no. C → B with C not a superkey directly violates BCNF.

(iii) If we decompose for BCNF into (C, B) and (A, C), the FD AB → C can no longer be checked inside a single table — the decomposition is lossless but NOT dependency preserving. This is the standard example showing why "BCNF always" is not free, and why 3NF is sometimes accepted instead.

Answer:

(i) A কোনো FD-র ডান পাশে নেই → A প্রতিটা key-তে থাকবে। \( A^+ = \{A\} \), তাই শুধু A fail। \( (AB)^+ = \{A,B,C\} \) ✓ আর \( (AC)^+ \): C→B থেকে B → \{A,B,C\} ✓। Candidate key: AB আর AC। তাই প্রতিটা attribute-ই prime।

(ii) 3NF: হ্যাঁ। C → B FD-র বাম পাশ superkey না, কিন্তু B একটা prime attribute — 3NF allow করে: key → যেকোনো কিছু অথবা যেকোনো কিছু → prime। BCNF: না। C superkey না হয়েও C → B আছে — এটা সরাসরি BCNF ভাঙে।

(iii) BCNF-এর জন্য (C, B) আর (A, C)-তে ভাঙলে AB → C FD-টা আর কোনো এক table-এর ভিতরে check করা যায় না — decomposition-টা lossless কিন্তু dependency preserving না। এটাই standard example যা দেখায় "সবসময় BCNF" free না, আর কখনো কখনো 3NF-ই মেনে নেওয়া হয়।

Q14. (Written) Schedule: R1(A), R2(A), W2(A), R3(B), W1(B), R2(B). Draw the precedence graph and decide if the schedule is conflict serializable. If yes, give the serial order.
Q14. (Written) Schedule: R1(A), R2(A), W2(A), R3(B), W1(B), R2(B)। Precedence graph আঁকুন আর schedule-টা conflict serializable কি না বলুন। হলে serial order দিন।
Show Answerউত্তর দেখুন
Answer:

List the conflict pairs (same item, different transactions, at least one write):

  • On A: R1(A) before W2(A) → edge T1 → T2. (R2–W2 are the same transaction — no edge.)
  • On B: R3(B) before W1(B) → edge T3 → T1; W1(B) before R2(B) → edge T1 → T2.

Graph edges: T3 → T1, T1 → T2. No cycle → conflict serializable. Topological order: T3, T1, T2 — the schedule behaves exactly like running T3 fully, then T1, then T2.

Answer:

Conflict pair-গুলো বের করুন (একই item, আলাদা transaction, অন্তত একটা write):

  • A-তে: R1(A) আগে, W2(A) পরে → edge T1 → T2। (R2–W2 একই transaction-এর — edge হয় না।)
  • B-তে: R3(B) আগে, W1(B) পরে → edge T3 → T1; W1(B) আগে, R2(B) পরে → edge T1 → T2

Graph-এর edge: T3 → T1, T1 → T2। কোনো cycle নেই → conflict serializable। Topological order: T3, T1, T2 — schedule-টা ঠিক T3 পুরো, তারপর T1, তারপর T2 চালানোর মতো আচরণ করে।

Q15. (Written) A B+ tree index node fits at most 4 pointers (so at most 3 keys). (i) What is the maximum number of leaf pointers reachable with height 3 (root at height 0 counts as level 1, i.e., 3 levels of internal nodes)? (ii) Why does a database prefer this B+ tree over a binary search tree for 1 million records?
Q15. (Written) একটা B+ tree-র index node-এ সর্বোচ্চ ৪টা pointer ধরে (মানে সর্বোচ্চ ৩টা key)। (i) ৩ level internal node দিয়ে (root ধরে) সর্বোচ্চ কয়টা leaf pointer-এ পৌঁছানো যায়? (ii) ১০ লাখ record-এর জন্য database কেন binary search tree-র বদলে এই B+ tree পছন্দ করবে?
Show Answerউত্তর দেখুন
Answer:

(i) Each internal node fans out to at most 4 children. With 3 levels of internal nodes: \( 4 \times 4 \times 4 = 64 \) leaf pointers. In general, height \( h \) with fan-out \( f \) reaches \( f^h \) leaves — growth is exponential in height.

(ii) A binary search tree has fan-out 2, so 1 million records need height about \( \log_2 10^6 \approx 20 \) — about 20 disk reads per search. A real B+ tree node fills a whole disk block (fan-out 100+), so height is about \( \log_{100} 10^6 = 3 \) — about 3 disk reads. Since disk reads dominate cost, fewer levels = much faster. Plus, linked leaves give cheap range scans, which a plain BST does not.

Answer:

(i) প্রতিটা internal node সর্বোচ্চ ৪টা child-এ যায়। ৩ level internal node হলে: \( 4 \times 4 \times 4 = 64 \)টা leaf pointer। সাধারণভাবে, fan-out \( f \) আর height \( h \) হলে \( f^h \)টা leaf — height-এর সাথে exponential বৃদ্ধি।

(ii) Binary search tree-র fan-out 2, তাই ১০ লাখ record-এ height প্রায় \( \log_2 10^6 \approx 20 \) — প্রতি search-এ প্রায় ২০টা disk read। আসল B+ tree-র এক node পুরো একটা disk block ভরে (fan-out ১০০+), তাই height প্রায় \( \log_{100} 10^6 = 3 \) — মাত্র ৩টা disk read। খরচের বড় অংশই disk read, তাই কম level মানে অনেক faster। তার উপর linked leaf-এ range scan সস্তা, যা সাধারণ BST-তে নেই।

Q16. (Written — Real exam style) (i) What is a trigger? When does it run, and what do BEFORE/AFTER and row-level/statement-level mean? (ii) Table Employee(emp_id, name, salary). Write a trigger that records every salary change into SalaryLog(emp_id, old_salary, new_salary, changed_on). (iii) Give one case where using a trigger is a bad idea.
Q16. (Written — Real exam style) (i) Trigger কী? এটা কখন চলে, আর BEFORE/AFTER এবং row-level/statement-level মানে কী? (ii) Table Employee(emp_id, name, salary)। এমন একটা trigger লিখুন যা প্রতিটা salary change SalaryLog(emp_id, old_salary, new_salary, changed_on)-এ record করে। (iii) কোন একটা case বলুন যেখানে trigger ব্যবহার করা খারাপ idea।
Show Answerউত্তর দেখুন
Answer:

(i) A trigger is a stored program attached to a table that the database fires automatically when an INSERT, UPDATE, or DELETE happens — nobody calls it by hand. BEFORE triggers run before the change is applied (good for validating or fixing values); AFTER triggers run after it (good for logging). A row-level trigger (FOR EACH ROW) fires once per changed row and can read OLD and NEW values; a statement-level trigger fires once per statement, however many rows changed.

-- (ii)
CREATE TRIGGER salary_audit
AFTER UPDATE ON Employee
FOR EACH ROW
WHEN (NEW.salary <> OLD.salary)
BEGIN
  INSERT INTO SalaryLog(emp_id, old_salary, new_salary, changed_on)
  VALUES (OLD.emp_id, OLD.salary, NEW.salary, CURRENT_DATE);
END;

Why each choice: AFTER — we log only changes that really happened; FOR EACH ROW — each employee gets their own log row; the WHEN guard skips updates that did not touch the salary; OLD/NEW give the values before and after.

(iii) Bad idea: putting heavy business logic in triggers. They run invisibly, so bugs are hard to trace; a trigger can fire another trigger and create chains or loops; and a row-level trigger on a bulk UPDATE of a million rows fires a million times.

Answer:

(i) Trigger হলো table-এর সাথে লাগানো একটা stored program, যা INSERT, UPDATE বা DELETE ঘটলে database নিজে থেকেই fire করে — কেউ হাতে call করে না। BEFORE trigger change apply হওয়ার আগে চলে (value validate বা ঠিক করার জন্য ভালো); AFTER trigger পরে চলে (log-এর জন্য ভালো)। Row-level trigger (FOR EACH ROW) প্রতিটা বদলানো row-এর জন্য একবার fire হয়, আর OLD/NEW value পড়তে পারে; statement-level trigger প্রতি statement-এ একবারই fire হয়, যত row-ই বদলাক।

-- (ii)
CREATE TRIGGER salary_audit
AFTER UPDATE ON Employee
FOR EACH ROW
WHEN (NEW.salary <> OLD.salary)
BEGIN
  INSERT INTO SalaryLog(emp_id, old_salary, new_salary, changed_on)
  VALUES (OLD.emp_id, OLD.salary, NEW.salary, CURRENT_DATE);
END;

প্রতিটা choice-এর কারণ: AFTER — শুধু সত্যিই ঘটে যাওয়া change log হয়; FOR EACH ROW — প্রতিটা employee-র নিজের log row হয়; WHEN guard সেই update বাদ দেয় যেখানে salary বদলায়নি; OLD/NEW দেয় আগের আর পরের value।

(iii) খারাপ idea: ভারী business logic trigger-এ রাখা। এগুলো অদৃশ্যভাবে চলে, তাই bug খুঁজে বের করা কঠিন; এক trigger আরেকটা fire করে chain বা loop বানাতে পারে; আর ১০ লাখ row-এর bulk UPDATE-এ row-level trigger ১০ লাখ বার fire হয়।

Q17. (Written — Real exam style) Build a B+ tree of order \( n = 4 \) (at most 3 keys per node) by inserting 10, 20, 5, 15, 25, 30, 8 in this order. Show the tree after every split. On a split of 4 keys, keep the first \( \lceil 4/2 \rceil = 2 \) keys in the left leaf and copy the first key of the right leaf up.
Q17. (Written — Real exam style) Order \( n = 4 \)-এর একটা B+ tree বানান (প্রতি node-এ সর্বোচ্চ ৩টা key), এই order-এ insert করে: 10, 20, 5, 15, 25, 30, 8। প্রতিটা split-এর পরে tree দেখান। ৪ key-এর split-এ প্রথম \( \lceil 4/2 \rceil = 2 \)টা key বাম leaf-এ রাখুন আর ডান leaf-এর প্রথম key উপরে copy করুন।
Show Answerউত্তর দেখুন
Answer:

Insert 10, 20, 5: one leaf, sorted: [5 10 20] (3 keys = full but legal).

Insert 15 — SPLIT 1: {5, 10, 15, 20} overflows. Left keeps [5 10], right gets [15 20], copy 15 up as a new root.

        [15]
       /    \
 [5 10] -> [15 20]

Insert 25: 25 ≥ 15 → right leaf: [15 20 25].

Insert 30 — SPLIT 2: {15, 20, 25, 30} overflows. Left keeps [15 20], right gets [25 30], copy 25 into the root.

        [15 | 25]
       /    |     \
 [5 10] -> [15 20] -> [25 30]

Insert 8: 8 < 15 → first leaf: [5 8 10]. Final tree:

        [15 | 25]
       /    |     \
 [5 8 10] -> [15 20] -> [25 30]

Checks: all 7 keys are in the leaves (3 + 2 + 2 = 7), leaves are sorted and linked, the copied-up keys 15 and 25 still appear in leaves, and every leaf has at least \( \lceil 3/2 \rceil = 2 \) keys.

Answer:

Insert 10, 20, 5: একটা leaf, sorted: [5 10 20] (৩ key = full কিন্তু বৈধ)।

Insert 15 — SPLIT 1: {5, 10, 15, 20} overflow করে। বাম রাখে [5 10], ডান পায় [15 20], 15 copy হয়ে নতুন root হয়।

        [15]
       /    \
 [5 10] -> [15 20]

Insert 25: 25 ≥ 15 → ডান leaf: [15 20 25]

Insert 30 — SPLIT 2: {15, 20, 25, 30} overflow করে। বাম রাখে [15 20], ডান পায় [25 30], 25 copy হয়ে root-এ যায়।

        [15 | 25]
       /    |     \
 [5 10] -> [15 20] -> [25 30]

Insert 8: 8 < 15 → প্রথম leaf: [5 8 10]। Final tree:

        [15 | 25]
       /    |     \
 [5 8 10] -> [15 20] -> [25 30]

Check: ৭টা key-ই leaf-এ আছে (3 + 2 + 2 = 7), leaf-গুলো sorted আর linked, উপরে copy হওয়া 15 আর 25 leaf-এও আছে, আর প্রতিটা leaf-এ কমপক্ষে \( \lceil 3/2 \rceil = 2 \)টা key আছে।

Q18. (Written — Real exam style) A server has 6 disks of 2 TB each. For RAID 0, RAID 1 (as RAID 10), RAID 5, and RAID 6: (i) compute the usable capacity, (ii) state how many disk failures each setup can survive, and (iii) say which level you would pick for a database that must survive one disk failure with the most usable space, and why.
Q18. (Written — Real exam style) একটা server-এ ২ TB-এর ৬টা disk আছে। RAID 0, RAID 1 (RAID 10 হিসেবে), RAID 5 আর RAID 6-এর জন্য: (i) usable capacity বের করুন, (ii) প্রতিটা setup কয়টা disk failure সহ্য করতে পারে বলুন, আর (iii) যে database-কে ১টা disk failure সহ্য করতে হবে আর সবচেয়ে বেশি usable space চাই — তার জন্য কোন level নেবেন, কেন?
Show Answerউত্তর দেখুন
Answer:

Raw total = 6 × 2 = 12 TB.

(i) Usable capacity:

  • RAID 0: n·d = 6 × 2 = 12 TB (all data, no redundancy).
  • RAID 10: (n/2)·d = 3 × 2 = 6 TB (3 mirror pairs).
  • RAID 5: (n−1)·d = 5 × 2 = 10 TB (one disk's worth of parity).
  • RAID 6: (n−2)·d = 4 × 2 = 8 TB (two disks' worth of parity).

(ii) Failure tolerance: RAID 0 = 0 (one failure destroys everything). RAID 10 = 1 per mirror pair (best case 3, but two failures in the SAME pair lose data — so guaranteed only 1). RAID 5 = exactly 1. RAID 6 = any 2.

(iii) Pick RAID 5. It survives the required 1 disk failure and gives 10 TB usable — more than RAID 10 (6 TB) and RAID 6 (8 TB). The trade-off is slower writes (parity update) and a risky rebuild window, but the question only demands 1-failure tolerance with maximum space, and RAID 5 wins that exactly.

Answer:

Raw মোট = 6 × 2 = 12 TB।

(i) Usable capacity:

  • RAID 0: n·d = 6 × 2 = 12 TB (সবই data, কোনো redundancy নেই)।
  • RAID 10: (n/2)·d = 3 × 2 = 6 TB (৩টা mirror pair)।
  • RAID 5: (n−1)·d = 5 × 2 = 10 TB (এক disk-এর সমান parity)।
  • RAID 6: (n−2)·d = 4 × 2 = 8 TB (দুই disk-এর সমান parity)।

(ii) Failure tolerance: RAID 0 = 0 (১টা failure-এই সব শেষ)। RAID 10 = প্রতি mirror pair-এ ১টা (best case ৩টা, কিন্তু একই pair-এ ২টা failure হলে data হারায় — তাই guarantee শুধু ১টা)। RAID 5 = ঠিক ১টা। RAID 6 = যেকোনো ২টা।

(iii) RAID 5 নিন। এটা দরকারি ১টা disk failure সহ্য করে আর 10 TB usable দেয় — RAID 10 (6 TB) আর RAID 6 (8 TB)-এর চেয়ে বেশি। Trade-off হলো write একটু slow (parity update) আর rebuild-এর সময়টা risky, কিন্তু প্রশ্ন চেয়েছে শুধু ১-failure tolerance আর সর্বোচ্চ space — সেটা RAID 5-ই ঠিকঠাক দেয়।

Q19. (Written — Real exam style) Account balance A = 100. This schedule runs: R1(A), R2(A), A = A + 50 in T1, W1(A), A = A − 30 in T2, W2(A), then both commit. (i) What is the final value of A, and what SHOULD it be? (ii) Name the anomaly. (iii) Show how strict two-phase locking (strict 2PL) fixes the schedule, step by step.
Q19. (Written — Real exam style) Account balance A = 100। এই schedule চলে: R1(A), R2(A), T1-এ A = A + 50, W1(A), T2-এ A = A − 30, W2(A), তারপর দুজনেই commit করে। (i) A-এর final value কত, আর কত হওয়া উচিত ছিল? (ii) Anomaly-টার নাম বলুন। (iii) Strict two-phase locking (strict 2PL) কীভাবে schedule-টা ঠিক করে, ধাপে ধাপে দেখান।
Show Answerউত্তর দেখুন
Answer:

(i) Both transactions read A = 100. T1 writes 100 + 50 = 150. Then T2 writes 100 − 30 = 70 — computed from the stale 100, so it overwrites T1's 150. Final A = 70, but the correct value is 100 + 50 − 30 = 120.

(ii) This is the lost update anomaly: T1's write is overwritten and lost because T2 read the old value before T1 wrote.

(iii) With strict 2PL, each transaction takes an X lock on A before touching it and holds it until commit:

  1. T1: X-lock(A) granted → R1(A) = 100.
  2. T2: requests X-lock(A) → BLOCKED (T1 holds it). T2 waits.
  3. T1: A = 150, W1(A), commit → lock released.
  4. T2: X-lock(A) granted → R2(A) = 150 (fresh value!), A = 150 − 30 = 120, W2(A), commit.

Final A = 120 — correct. The schedule is now equivalent to serial T1 → T2, so no update is lost.

Answer:

(i) দুই transaction-ই A = 100 পড়ে। T1 লেখে 100 + 50 = 150। তারপর T2 লেখে 100 − 30 = 70 — পুরনো 100 থেকে হিসাব করা, তাই এটা T1-এর 150-কে overwrite করে। Final A = 70, কিন্তু সঠিক value 100 + 50 − 30 = 120

(ii) এটা lost update anomaly: T1-এর write overwrite হয়ে হারিয়ে যায়, কারণ T1 লেখার আগেই T2 পুরনো value পড়ে ফেলেছিল।

(iii) Strict 2PL-এ প্রতিটা transaction A-তে হাত দেওয়ার আগে X lock নেয় আর commit পর্যন্ত ধরে রাখে:

  1. T1: X-lock(A) পায় → R1(A) = 100।
  2. T2: X-lock(A) চায় → BLOCKED (T1 ধরে আছে)। T2 অপেক্ষা করে।
  3. T1: A = 150, W1(A), commit → lock ছাড়ে।
  4. T2: X-lock(A) পায় → R2(A) = 150 (fresh value!), A = 150 − 30 = 120, W2(A), commit।

Final A = 120 — সঠিক। Schedule-টা এখন serial T1 → T2-এর সমান, তাই কোনো update হারায় না।