Alumni Hands-on Workshop

SQL A to Z

Zero থেকে Confident — Real Example দিয়ে Database শেখা

NEmran Masud (Nayon)Instructor · Alumni
BMS BithiInstructor · Alumni
workshop.sql

  

Roadmap

আজ আমরা কোথায় যাচ্ছি

20 min
01

Foundation

Data → Database · Table, Key · SQL vs NoSQL · Sample DB

25 min
02

DDL

Data Types · CREATE · Constraints · ALTER / DROP

30 min
03

CRUD

INSERT · SELECT · UPDATE · DELETE

40 min
04

Querying

WHERE · ORDER BY · Aggregate · GROUP BY

40 min
05

JOINs

INNER · LEFT / RIGHT · SELF / CROSS · UNION

40 min
06

Advanced

Subquery · CASE · CTE · Window

40 min
07

Design

Relationship · Normalization · Index & View · Transaction

35 min
08

Pro Level

Permission · SQL Injection · Performance · Mistakes

☕
Module 04-এর পর ১৫ মিনিট break · মোট ~৪.৫ ঘণ্টা · চাইলে ২ দিন: Day-1 = 01–04, Day-2 = 05–08

Instructor Prep

Workshop-এর আগে যা যা লাগবে

💻

Software

  • MySQL 8 / XAMPP — আগের দিন install
  • Workbench / phpMyAdmin / DBeaver
  • Backup: এই deck-এর Run ▶
📦

Materials

  • এই presentation
  • university.sql sample DB
  • ১ পাতার cheat sheet + practice sheet
  • Feedback Google Form
🏫

Room & Team

  • Projector, mic, WiFi, extension
  • Nayon present → Bithi floor-এ debug (module অনুযায়ী swap)
  • Code share-এর group
⚠️
সবচেয়ে বেশি সময় নষ্ট হয় installation-এ। আগের দিন বলুন: SELECT VERSION(); চালিয়ে screenshot পাঠাও।

Instructor Prep

কিভাবে শেখাবো — প্রতিটা topic-এ ৫টা ধাপ

01

Story

"bKash কিভাবে জানে তোমার balance কত?"

02

Concept

সহজ analogy — Table = Excel sheet

03

Live Demo

Live লিখুন, ইচ্ছা করে ভুল করুন

04

You Do

Students ৫ মিনিট নিজে try করবে

05

Trade-off

Pros / Cons — কখন ব্যবহার করবো না

Golden Rules

  • I do → We do → You do
  • ১০ মিনিটের বেশি একটানা lecture না
  • একটাই database পুরো workshop জুড়ে

এড়িয়ে চলুন

  • Syntax মুখস্থ করানো
  • একসাথে ৩–৪টা নতুন concept
  • Slide পড়ে শোনানো
01

Module 01 / 08

Foundation

Database-এর ভিত্তি

Data → DatabaseTable, KeySQL vs NoSQLSample DB

Foundation

Data → Information → Database

🧩

Data

কাঁচা তথ্য — আলাদাভাবে অর্থহীন

Rahim, 3.75, CSE
💡

Information

Process করা, অর্থবহ data

"Rahim CSE-এর top 10%-এ আছে"

🗄️

Database

সাজানো data-র সংগ্রহ — দ্রুত খোঁজা, update ও share করা যায়

Real Example

রেজিস্টার খাতা / Excel vs Database

📒 খাতা / Excel

  • শুরু করা সহজ, ছোট data-য় যথেষ্ট
  • ১০,০০০ student হলে খুঁজতে কষ্ট
  • একসাথে ১০ জন edit করলে conflict
  • Duplicate বা ভুল data আটকানো যায় না

🗄️ Database

  • লাখো row-তে millisecond-এ search
  • হাজার user একসাথে
  • Rule দিয়ে ভুল data আটকায়
  • Backup, security, permission

Foundation

একটা Table-এর শরীরতত্ত্ব

id 🔑namecgpadept_id 🔗
1Rahim Uddin3.751
2Karim Hasan3.201
3Nusrat Jahan3.902

students table · dept_id → departments.id

Table
একটা বিষয়ের data — Excel sheet-এর মতো
Row
একটা record — একজন student (হাইলাইট)
Column
একটা attribute — name, cgpa
Primary Key 🔑
প্রতিটা row-এর unique পরিচয় — Student ID card
Foreign Key 🔗
অন্য table-এর PK-কে refer করে — দুই table-এর সেতু

Foundation

SQL কী?

Database-এর সাথে কথা বলার ভাষা।

Structured Query Language · IBM, ১৯৭০-এর দশক · এখন ANSI/ISO standard · Declarative — তুমি বলো কী চাও, কিভাবে আনবে সেটা database ঠিক করে।

-- "Dhaka-র সব student-এর নাম দাও"
SELECT name FROM students WHERE city = 'Dhaka';

Why SQL?

SQL কোথায় কোথায় চলছে

💸

bKash / Nagad

প্রতিটা transaction ও balance

🛒

Daraz / Chaldal

Product, order, inventory

🎓

University Portal

Result, registration, payment

📊

Data Analyst

Report, Power BI, dashboard

💼
Backend Dev, Data Analyst, Data Engineer, QA — প্রায় সব tech interview-এ SQL প্রশ্ন আসে।

Foundation

SQL vs NoSQL

SQL (Relational)NoSQL
StructureFixed schema — table, columnFlexible — JSON document
ExampleMySQL, PostgreSQL, SQL ServerMongoDB, Redis, Firebase
ScalingVertical (বড় server)Horizontal (অনেক server)
Transactionশক্তিশালী ACIDপ্রায়ই eventual consistency
JOINচমৎকারদুর্বল

কখন কোনটা?

সঠিক কাজে সঠিক Tool

🏦

SQL বেছে নাও যখন...

Banking, e-commerce order, university result — যেখানে data ভুল হলে চলবে না এবং relation অনেক।

💬

NoSQL বেছে নাও যখন...

Chat message, social feed, cache, real-time analytics — data বিশাল, structure বারবার বদলায়।

💡
বড় কোম্পানি দুটোই ব্যবহার করে — Order data PostgreSQL-এ, session/cache Redis-এ।

Foundation

জনপ্রিয় RDBMS

Databaseশক্তিদুর্বলতাকোথায়
MySQLFree, সহজ, XAMPP/PHP-এর সাথীFULL JOIN নেইWordPress
PostgreSQLFeature-rich, JSON, extensionConfig একটু জটিলInstagram
SQLiteServer লাগে না, একটা fileবেশি concurrent write পারে নাAndroid, browser
SQL ServerMicrosoft ecosystemLicense খরচBank, corporate
OracleEnterprise-grade reliabilityঅনেক দামিTelecom, বড় bank
🎯
আমরা শিখবো MySQL syntax। Deck-এর Run ▶ browser-এ SQLite দিয়ে চলে — ৯৫% syntax একই।

Our Playground

Sample Database — University

departments
  • id
  • name
  • building
students
  • id
  • name
  • email
  • dept_id
  • cgpa
  • city
  • admission_year
enrollments
  • id
  • student_id
  • course_id
  • semester
  • marks
courses
  • id
  • code
  • title
  • credit
  • dept_id
teachers
  • id
  • name
  • dept_id
  • salary
  • head_id
accounts
  • id
  • owner
  • balance

students → departments (Many-to-One) · students ⟷ courses via enrollments (Many-to-Many) · teachers.head_id → teachers (Self)

Try it

প্রথম Query — Run চাপো ▶

SELECT * FROM students;

Foundation

SQL Command-এর ৫টা পরিবার

DDL

Structure বানানো

CREATE ALTER DROP

🏗️ বাড়ির নকশা

DML

Data বদলানো

INSERT UPDATE DELETE

🛋️ ফার্নিচার রাখা

DQL

Data পড়া

SELECT

🔍 জিনিস খোঁজা

DCL

Permission

GRANT REVOKE

🔑 চাবি দেওয়া

TCL

Transaction

COMMIT ROLLBACK

💾 Save / Undo

⭐
Interview: DDL structure নিয়ে কাজ করে (auto-commit), DML data নিয়ে (rollback করা যায়)।
02

Module 02 / 08

DDL

Database-এর নকশা বানানো

Data TypesCREATEConstraintsALTER / DROP

DDL

Data Types — সঠিকটা বাছাই

Typeকিসের জন্যExample
INTপূর্ণ সংখ্যাid, credit, age
DECIMAL(10,2)নির্ভুল দশমিকটাকা, salary, price
VARCHAR(n)পরিবর্তনশীল textname, email, phone
CHAR(n)নির্দিষ্ট দৈর্ঘ্যcountry code 'BD'
TEXTবড় লেখাcomment, blog
DATE / DATETIMEতারিখ ও সময়dob, created_at
BOOLEAN / ENUMহ্যাঁ/না, নির্দিষ্ট valueis_active, status

Pitfall #1

FLOAT দিয়ে টাকা রাখবে না

0.1 + 0.2 =
0.30000000000000004

bKash-এ কোটি transaction-এ পয়সার হিসাব গরমিল। টাকা = সবসময় DECIMAL

Pitfall #2

Phone number কোন type-এ?

✗ INT

1712345678

শুরুর 0 হারিয়ে গেল!

✓ VARCHAR(15)

01712345678

Phone দিয়ে অঙ্ক করি না → text

DDL

CREATE DATABASE & TABLE

CREATE DATABASE university;
USE university;

CREATE TABLE students (
  id      INT AUTO_INCREMENT PRIMARY KEY,
  name    VARCHAR(100) NOT NULL,
  email   VARCHAR(100) UNIQUE,
  dept_id INT,
  cgpa    DECIMAL(3,2) CHECK (cgpa BETWEEN 0 AND 4),
  city    VARCHAR(50) DEFAULT 'Dhaka',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (dept_id) REFERENCES departments(id)
);
AUTO_INCREMENT
id নিজে 1, 2, 3...
PRIMARY KEY
unique + NOT NULL
NOT NULL
খালি রাখা যাবে না
UNIQUE
duplicate email না
CHECK
CGPA 0–4 এর বাইরে reject
DEFAULT
না দিলে এই value
FOREIGN KEY
অস্তিত্বহীন dept দেওয়া যাবে না

Good Habit

Naming Convention

Table

plural, snake_case

students course_teachers

Column

singular, অর্থবহ

dept_id created_at

Consistency

যেটাই বেছে নাও, পুরো project-এ এক রকম রাখো

DDL

Constraints — Database-এর পাহারাদার

ভুল data ঢোকার আগেই আটকে দেয়।

Application-এ bug থাকলেও database-এর data safe থাকে। চলো ভাঙার চেষ্টা করি ↓

Live: ভাঙার চেষ্টা

প্রতিটা Run করে error দেখো

INSERT INTO students (id, name, email, dept_id, cgpa)
VALUES (101, 'Test Student', 'test@uni.edu', 1, 3.5);
INSERT INTO students (id, name, cgpa)
VALUES (102, 'Fake Topper', 5.0);
INSERT INTO students (id, name, email)
VALUES (103, 'Copy Cat', 'rahim@uni.edu');
INSERT INTO students (id, email)
VALUES (104, 'noname@uni.edu');

Trade-off

Constraints — Pros & Cons

Pros

  • Garbage data ঢোকে না
  • App-এর bug থেকে সুরক্ষা
  • Business rule এক জায়গায়

Cons

  • প্রতিটা insert-এ check → সামান্য slow
  • Rule বদলাতে ALTER লাগে
  • Bulk import-এ ঝামেলা

DDL

ALTER — Structure বদলানো

ALTER TABLE students ADD phone VARCHAR(15);          -- column যোগ
ALTER TABLE students MODIFY name VARCHAR(150) NOT NULL; -- type বদল
ALTER TABLE students RENAME COLUMN phone TO mobile;   -- নাম বদল
ALTER TABLE students DROP COLUMN mobile;              -- column মুছে ফেলা

TRUNCATE TABLE enrollments;          -- সব data মুছবে, table থাকবে
DROP TABLE IF EXISTS enrollments;    -- পুরো table উধাও

Interview Favorite ⭐

DELETE vs TRUNCATE vs DROP

🪑

DELETE

কিছু ফার্নিচার সরানো

  • WHERE চলে ✓
  • Rollback ✓
  • ধীর · DML
🧹

TRUNCATE

সব ফার্নিচার ফেলে দেওয়া

  • WHERE ✗
  • Rollback ✗ (MySQL)
  • খুব দ্রুত · ID reset · DDL
💥

DROP

বাড়িটাই ভেঙে ফেলা

  • Table + structure উধাও
  • Rollback ✗
  • DDL
03

Module 03 / 08

CRUD

Data ঢোকানো, পড়া, বদলানো, মোছা

INSERTSELECTUPDATEDELETE

CRUD · Create

INSERT — Data ঢোকানো

-- একটা row, column নাম সহ (best practice)
INSERT INTO students (id, name, email, dept_id, cgpa, city)
VALUES (11, 'Rafiq Islam', 'rafiq@uni.edu', 1, 3.40, 'Cumilla');

-- একসাথে অনেক row (দ্রুত)
INSERT INTO courses (id, code, title, credit, dept_id) VALUES
  (7, 'CSE310', 'Web Engineering', 3, 1),
  (8, 'BBA210', 'Marketing', 3, 3);

SELECT * FROM courses;

Trade-off

Column নাম লিখবো নাকি লিখবো না?

INSERT INTO t (a, b) VALUES

  • নতুন column যোগ হলেও query ভাঙে না
  • পড়তে সহজ, ভুল কম

INSERT INTO t VALUES

  • Column order মুখস্থ রাখতে হয়
  • Table বদলালে সব query ভাঙে
⚡
১০,০০০ row? আলাদা ১০,০০০ INSERT না — multi-row INSERT বা CSV থেকে LOAD DATA INFILE → ১০–১০০ গুণ দ্রুত।

CRUD · Read

SELECT — Data পড়া

SELECT name AS student_name,
       cgpa,
       cgpa * 25 AS percentage,
       'BD'      AS country
FROM students;

Trade-off

SELECT * — বন্ধু নাকি শত্রু?

কখন ঠিক আছে

  • Data explore / debug করার সময়
  • ছোট table দেখতে

Production-এ এড়িয়ে চলো

  • অপ্রয়োজনীয় data → network slow
  • password column-ও চলে আসবে 😱
  • Covering index-এর সুবিধা নষ্ট
🛒
Daraz-এর product list-এ শুধু name, price, image লাগে — ৫০০০ অক্ষরের description আনার দরকার নেই।

CRUD · Update & Delete

Data বদলানো ও মোছা

UPDATE students
SET cgpa = 3.80, city = 'Gazipur'
WHERE id = 1;

SELECT id, name, cgpa, city
FROM students WHERE id = 1;
DELETE FROM enrollments
WHERE id = 15;

SELECT COUNT(*) AS total
FROM enrollments;

Danger Zone

UPDATE students SET cgpa = 4.0;

WHERE নেই → সব student-এর CGPA 4.0 😱

Industry Habit

নিরাপদে Update/Delete করার ৫ ধাপ

01

SELECT আগে

একই WHERE দিয়ে দেখো কোন row

02

Transaction

ভুল হলে ROLLBACK

03

Safe mode

SQL_SAFE_UPDATES=1

04

Backup

Production-এ আগে backup

05

Soft delete

deleted_at set — আসলে মুছো না

💡
Facebook-এ "deleted" post ৩০ দিন পর্যন্ত ফেরত আনা যায় — এটাই soft delete।
04

Module 04 / 08

Querying

প্রশ্ন করার শিল্প

WHEREORDER BYAggregateGROUP BY

Querying

WHERE — Filter করার Operator

OperatorমানেExample
= != <>সমান / অসমানcity = 'Dhaka'
> < >= <=তুলনাcgpa >= 3.5
AND OR NOTশর্ত জোড়াa AND (b OR c)
BETWEENrange (দুই পাশ সহ)BETWEEN 3 AND 3.5
INlist-এর যেকোনোটাIN ('Dhaka','Sylhet')
LIKEpatternname LIKE 'S%'
IS NULLখালি কিনাcgpa IS NULL

Live

WHERE in action

-- CSE-র 3.5+ student
SELECT name, cgpa
FROM students
WHERE dept_id = 1
  AND cgpa >= 3.5;
SELECT name, city, admission_year
FROM students
WHERE city IN ('Dhaka','Chattogram')
  AND admission_year
      BETWEEN 2022 AND 2023;
⚠️
AND আগে evaluate হয় OR-এর চেয়ে। সন্দেহ হলে ( ) দাও।

Pattern

LIKE wildcard

'S%'S দিয়ে শুরু
'%an'an দিয়ে শেষ
'%Islam%'যেকোনো জায়গায়
'_a%'দ্বিতীয় অক্ষর a
SELECT name, email FROM students
WHERE name LIKE '%a%n'
   OR email LIKE 's%';
⚡
LIKE '%abc' index ব্যবহার করতে পারে না → বড় table-এ slow

The NULL Trap

NULL মানে "জানা নেই"

NULL ≠ 0  ·  NULL ≠ ''  ·  NULL ≠ NULL

Imran নতুন ভর্তি — CGPA এখনো নেই। এটা 0 না, এটা অজানা।

The NULL Trap

ভুল vs সঠিক

-- কোনো row আসবে না!
SELECT name FROM students
WHERE cgpa = NULL;
SELECT name FROM students
WHERE cgpa IS NULL;

SELECT name, COALESCE(cgpa, 0) AS cgpa
FROM students;

Querying

ORDER BY · LIMIT · DISTINCT

-- Top 3 student
SELECT name, cgpa FROM students
WHERE cgpa IS NOT NULL
ORDER BY cgpa DESC
LIMIT 3;
-- কোন কোন শহর থেকে
SELECT DISTINCT city
FROM students
ORDER BY city;
⚠️
ORDER BY ছাড়া result-এর order-এর কোনো গ্যারান্টি নেই।

Real Example

Daraz-এর Pagination

SELECT id, name, price FROM products
ORDER BY created_at DESC
LIMIT 20 OFFSET 40;      -- page 3 = (3 - 1) × 20

LIMIT / OFFSET

  • লেখা সহজ
  • যেকোনো page-এ jump

OFFSET 100000?

  • ১ লাখ row পড়ে ফেলে দেয় → slow
  • সমাধান: keyset — WHERE id < last_id (Facebook feed)

Querying

Aggregate — হিসাব-নিকাশ

Functionকাজ
COUNT(*)মোট row
COUNT(col)NULL বাদে
SUMযোগফল
AVGগড় (NULL বাদ!)
MIN / MAXসর্বনিম্ন / সর্বোচ্চ
SELECT COUNT(*)             AS total,
       COUNT(cgpa)          AS with_cgpa,
       ROUND(AVG(cgpa), 2)  AS avg_cgpa,
       MAX(cgpa)            AS highest,
       COUNT(DISTINCT city) AS cities
FROM students;
🤔
COUNT(*) আর COUNT(cgpa) আলাদা কেন? — Imran-এর CGPA NULL!

Querying

GROUP BY — দলে ভাগ করে হিসাব

🏫 সব student-কে department অনুযায়ী আলাদা রুমে পাঠাও…

🔢 প্রতিটা রুমে গুনে দেখো…

🚪 ছোট রুমগুলো বাদ দাও (HAVING)

SELECT dept_id,
       COUNT(*)            AS total,
       ROUND(AVG(cgpa), 2) AS avg_cgpa
FROM students
GROUP BY dept_id
HAVING COUNT(*) >= 2;

Interview Favorite ⭐

WHERE vs HAVING

WHEREHAVING
কখনGroup-এর আগেGroup-এর পরে
কিসের উপরপ্রতিটা rowপ্রতিটা group
Aggregate✗✓
SELECT dept_id, ROUND(AVG(cgpa),2) AS avg
FROM students
WHERE admission_year >= 2022  -- row
GROUP BY dept_id
HAVING AVG(cgpa) >= 3.3;      -- group
📏
SELECT-এ যে column aggregate না, সেটা অবশ্যই GROUP BY-তে থাকবে।

Mind-blowing 🤯

Query আসলে কোন order-এ চলে?

1FROMtable
›
2WHERErow filter
›
3GROUP BYদল
›
4HAVINGদল filter
›
5SELECTcolumn, alias
›
6ORDER BYsort
›
7LIMITকতগুলো
💡
তাই WHERE-এ alias চলে না (তখনো SELECT চলেনি), কিন্তু ORDER BY-তে চলে। এটা বুঝলে অর্ধেক error নিজেই ধরতে পারবে।
05

Module 05 / 08

JOINs

Table জোড়া লাগানো

INNERLEFT / RIGHTSELF / CROSSUNION

JOINs

৪ ধরনের JOIN — এক নজরে

AB

INNER

দুই পাশেই মিল

AB

LEFT

A-এর সব + B-এর মিল

AB

RIGHT

B-এর সব + A-এর মিল

AB

FULL

দুই পাশের সব

🎉
INNER = যারা party-তে এসেছে এবং gift এনেছে · LEFT = সব অতিথি, gift আনুক বা না আনুক

JOINs

INNER JOIN — শুধু মিলে যাওয়া

SELECT s.name, s.cgpa,
       d.name AS department
FROM students s
INNER JOIN departments d
        ON s.dept_id = d.id
ORDER BY d.name;
students s
alias — ছোট নাম
ON
কোন column দিয়ে মেলাবো
d.name
দুই table-এই "name" → prefix জরুরি
👀
Arif (dept নেই) আর Pharmacy (student নেই) — কেউই result-এ নেই। কেন?

Danger Zone

ON ভুলে গেলে?

11 × 5 = 55 rows

Cartesian product — প্রতিটা student প্রতিটা department-এ 🤯

JOINs

LEFT JOIN — যা নেই, সেটাও দেখাও

SELECT d.name AS department,
       COUNT(s.id) AS students
FROM departments d
LEFT JOIN students s
       ON s.dept_id = d.id
GROUP BY d.id, d.name;
-- কেউ enroll করেনি এমন course
SELECT c.code, c.title
FROM courses c
LEFT JOIN enrollments e
       ON e.course_id = c.id
WHERE e.id IS NULL;

Real Use Case

"যা নেই" খুঁজে বের করার জাদু

🛒

Daraz

৬ মাসে কোনো order করেনি এমন customer → discount SMS

🎓

University

যারা এখনো course register করেনি → reminder

🏢

HR

আজ attendance নেই এমন employee

💡
RIGHT JOIN = table উল্টে LEFT JOIN। তাই industry-তে প্রায় সবাই শুধু LEFT JOIN লেখে।

JOINs

FULL · CROSS · SELF

SELECT s.name, d.name AS dept
FROM students s
FULL OUTER JOIN departments d
  ON s.dept_id = d.id;

MySQL-এ নেই → LEFT JOIN … UNION … RIGHT JOIN

SELECT s.v AS size, c.v AS color
FROM (SELECT 'S' v UNION SELECT 'M') s
CROSS JOIN
 (SELECT 'Red' v UNION
  SELECT 'Black') c;

T-shirt-এর সব variant

SELECT t.name AS teacher,
       h.name AS head
FROM teachers t
LEFT JOIN teachers h
  ON t.head_id = h.id;

Employee → Manager, category → parent

JOINs

৪টা Table একসাথে — Result Sheet

SELECT s.name AS student, d.name AS dept, c.title AS course, e.semester, e.marks
FROM enrollments e
JOIN students s       ON s.id = e.student_id
JOIN courses  c       ON c.id = e.course_id
LEFT JOIN departments d ON d.id = s.dept_id
ORDER BY s.name, e.semester;

Combine Rows

UNION vs UNION ALL

SELECT name, 'Student' AS role
FROM students
UNION ALL
SELECT name, 'Teacher'
FROM teachers;
UNIONUNION ALL
Duplicate বাদসব রাখে
Slow (compare করে)Fast ⚡
↔️
JOIN = column পাশাপাশি জোড়ে
UNION = row নিচে নিচে জোড়ে ↕️
06

Module 06 / 08

Advanced

Pro-দের মতো Query

SubqueryCASECTEWindow

Advanced

Subquery — Query-র ভিতরে Query

-- সর্বোচ্চ CGPA কার?
SELECT name, cgpa FROM students
WHERE cgpa = (SELECT MAX(cgpa)
              FROM students);
-- কারা Database Systems নিয়েছে?
SELECT name FROM students
WHERE id IN (SELECT student_id
             FROM enrollments
             WHERE course_id = 2);

Advanced

Correlated Subquery & EXISTS

-- নিজের dept-এর গড়ের চেয়ে ভালো
SELECT s.name, s.cgpa FROM students s
WHERE s.cgpa > (
  SELECT AVG(cgpa) FROM students
  WHERE dept_id = s.dept_id);
-- অন্তত একটা 90+ marks
SELECT name FROM students s
WHERE EXISTS (
  SELECT 1 FROM enrollments e
  WHERE e.student_id = s.id
    AND e.marks >= 90);

Trade-off

Subquery — Pros & Cons

Pros

  • ধাপে ধাপে চিন্তা — পড়তে সহজ
  • MAX/AVG-এর সাথে তুলনায় দারুণ

Cons

  • Correlated → প্রতি row-এ চলে → slow
  • JOIN/CTE দিয়ে প্রায়ই দ্রুত হয়
  • ৩–৪ level nested হলে পড়া কঠিন

Advanced

CASE WHEN — SQL-এর if-else

SELECT s.name, c.code, e.marks,
  CASE
    WHEN e.marks >= 80 THEN 'A+'
    WHEN e.marks >= 70 THEN 'A-'
    WHEN e.marks >= 60 THEN 'B'
    WHEN e.marks >= 40 THEN 'D'
    ELSE 'F'
  END AS grade
FROM enrollments e
JOIN students s ON s.id = e.student_id
JOIN courses  c ON c.id = e.course_id
ORDER BY e.marks DESC;
🎓
Students-রা নিজেদের grading দেখে সবচেয়ে মজা পায়!
⚠️
প্রথম যে WHEN true, সেটাই নেয় → বড় থেকে ছোট লেখো।
🌍
Order status, salary band, label বানানো

Pro Trick

CASE + SUM = এক Query-তে Dashboard

SELECT
  SUM(CASE WHEN cgpa >= 3.5 THEN 1 ELSE 0 END)                AS excellent,
  SUM(CASE WHEN cgpa >= 3.0 AND cgpa < 3.5 THEN 1 ELSE 0 END) AS good,
  SUM(CASE WHEN cgpa < 3.0 THEN 1 ELSE 0 END)                 AS needs_help,
  SUM(CASE WHEN cgpa IS NULL THEN 1 ELSE 0 END)               AS new_student
FROM students;

Advanced

Built-in Functions

🔤 String

SELECT UPPER(name),
  LENGTH(name) AS len,
  SUBSTR(email, 1,
   INSTR(email,'@')-1) AS user
FROM students LIMIT 3;

🔢 Number

SELECT ROUND(3.14159, 2),
       ABS(-5),
       10 % 3 AS modulo;

📅 Date

SELECT NOW(), CURDATE(),
 DATEDIFF(NOW(), dob)/365,
 DATE_FORMAT(created_at,
   '%d %b %Y')
FROM students;
🔎
Function মুখস্থ করতে হবে না — কী আছে জানলেই যথেষ্ট, বাকিটা documentation।

Advanced

CTE (WITH) — রান্নার রেসিপির মতো ধাপে ধাপে

WITH student_avg AS (                       -- ধাপ ১: প্রতিজনের গড়
  SELECT student_id, ROUND(AVG(marks),1) AS avg_marks
  FROM enrollments GROUP BY student_id
),
toppers AS (                                -- ধাপ ২: ৭৫+ বাছাই
  SELECT * FROM student_avg WHERE avg_marks >= 75
)
SELECT s.name, t.avg_marks                  -- ধাপ ৩: নাম জোড়া
FROM toppers t JOIN students s ON s.id = t.student_id
ORDER BY t.avg_marks DESC;

Trade-off

CTE — Pros & Cons

Pros

  • উপর থেকে নিচে গল্পের মতো পড়া যায়
  • একই result বারবার ব্যবহার
  • Recursive CTE → org chart, category tree

Cons

  • MySQL 8.0-এর আগে নেই
  • কিছু DB-তে re-compute হয়
  • Query-র বাইরে reuse নয় (তখন View)

Advanced

Window Functions — row রেখেই হিসাব

SELECT name, dept_id, cgpa,
  RANK() OVER (ORDER BY cgpa DESC)
    AS overall_rank,
  ROUND(AVG(cgpa) OVER
    (PARTITION BY dept_id), 2)
    AS dept_avg
FROM students
WHERE cgpa IS NOT NULL;
CGPAROW_NUMRANKDENSE
3.9111
3.9211
3.7332

Interview Classic ⭐

প্রতিটা Department-এর Topper

WITH ranked AS (
  SELECT name, dept_id, cgpa,
    DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY cgpa DESC) AS rnk
  FROM students WHERE dept_id IS NOT NULL
)
SELECT * FROM ranked WHERE rnk = 1;

LAG()/LEAD() = আগের/পরের row (মাসিক growth) · SUM() OVER (ORDER BY date) = running balance (bKash statement)

07

Module 07 / 08

Design

ভালো Database Design

RelationshipNormalizationIndex & ViewTransaction

Design

Relationship — ৩ ধরনের সম্পর্ক

One-to-One

Student—ID Card

একজনের একটাই card। Sensitive data আলাদা রাখতে।

One-to-Many

Dept—<Students

"Many" দিকে FK থাকে → students.dept_id

Many-to-Many

Students>—<Courses

মাঝে junction table → enrollments

Junction Table

Many-to-Many বানানো

CREATE TABLE enrollments (
  id         INT AUTO_INCREMENT PRIMARY KEY,
  student_id INT NOT NULL,
  course_id  INT NOT NULL,
  semester   VARCHAR(20),
  marks      INT,
  UNIQUE (student_id, course_id, semester),     -- একই semester-এ duplicate না
  FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
  FOREIGN KEY (course_id)  REFERENCES courses(id)
);
⚠️
ON DELETE CASCADE — student মুছলে তার enrollments-ও মুছে যাবে। সুবিধাজনক, কিন্তু বিপজ্জনক।

Design

Normalization — সমস্যাটা দেখো

studentdeptbuildingcourses
RahimCSEBlock ACSE101, CSE220, ENG101
KarimCSEBlock ACSE101, CSE220
1
এক cell-এ অনেক value
2
"Block A" বারবার — বদলাতে ১০০ জায়গায় update
3
নতুন dept-এ student না থাকলে building রাখার জায়গা নেই

Design

1NF → 2NF → 3NF

1NF

প্রতিটা cell-এ একটাই value। Course list → আলাদা row-তে।

2NF

প্রতিটা column পুরো key-এর উপর নির্ভর। Course title → courses table।

3NF

Transitive dependency নেই। building নির্ভর dept-এর উপর → departments table।

"The key, the whole key, and nothing but the key."

Trade-off

Normalize নাকি Denormalize?

Normalization

  • Duplicate নেই → storage কম
  • এক জায়গায় update
  • Data inconsistent হয় না

Cost

  • অনেক JOIN → read slow হতে পারে
  • Report-এ ইচ্ছা করে denormalize (Data Warehouse)
📏
Rule: আগে 3NF, পরে performance দরকার হলে denormalize।

Design

Index — বইয়ের সূচিপত্র

📖

Without Index

৫০০ পাতা উল্টে "JOIN" খোঁজা
Full Table Scan

⚡

With Index

সূচিপত্র দেখে সরাসরি পাতা ১২৩
Index Seek · ১০ সেকেন্ড → ১০ ms

CREATE INDEX idx_students_city ON students(city);

EXPLAIN QUERY PLAN                     -- MySQL: EXPLAIN SELECT ...
SELECT * FROM students WHERE city = 'Dhaka';

Trade-off

Index — কোথায় দেবে, কোথায় না

Pros

  • SELECT, WHERE, JOIN, ORDER BY দ্রুত
  • UNIQUE index duplicate আটকায়
  • দাও: email, phone, foreign key

Cons

  • INSERT/UPDATE/DELETE slow
  • Extra disk space
  • দিও না: ছোট table, gender-এর মতো কম value
⬅️
Leftmost rule: (a, b) index → WHERE a চলবে, শুধু WHERE b চলবে না।

Design

VIEW — Save করা Query

CREATE VIEW result_sheet AS
SELECT s.name AS student, c.title AS course,
       e.semester, e.marks
FROM enrollments e
JOIN students s ON s.id = e.student_id
JOIN courses  c ON c.id = e.course_id;

SELECT * FROM result_sheet
WHERE marks >= 80 ORDER BY marks DESC;

Pros

  • জটিল join একটা নামে
  • Security: salary লুকিয়ে দেখাও
  • সবাই একই logic

Cons

  • View-এর উপর view → slow
  • সাধারণত update করা যায় না

Design

Transaction — bKash Send Money

Rahim → Karim ৳১০০০

STEP 1

Rahim থেকে কাটো

5000 → 4000

💥 CRASH

Server বন্ধ!

টাকা কোথায় গেল?

STEP 2

Karim-এ যোগ

কখনো হলো না…

সমাধান: দুটো কাজ একসাথে — হয় দুটোই হবে, নয় কোনোটাই না।

Live

COMMIT ও ROLLBACK

BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 1000
WHERE id = 1;
UPDATE accounts SET balance = balance + 1000
WHERE id = 2;
COMMIT;

SELECT * FROM accounts;
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 3000
WHERE id = 1;
SELECT * FROM accounts; -- Karim পায়নি!
ROLLBACK;               -- সব আগের মতো

SELECT * FROM accounts;

Interview Favorite ⭐

ACID — Transaction-এর ৪ প্রতিশ্রুতি

A

Atomicity

হয় সব, নয় কিছুই না — অর্ধেক টাকা হারাবে না

C

Consistency

Rule ভাঙবে না — মোট টাকা আগে-পরে সমান

I

Isolation

একসাথে চলা transaction একে অপরকে বিরক্ত করবে না

D

Durability

COMMIT হলে বিদ্যুৎ গেলেও data থাকবে

Design

Procedure · Function · Trigger

DELIMITER //
CREATE PROCEDURE GetDeptStudents(IN p_dept INT)
BEGIN
  SELECT name, cgpa FROM students
  WHERE dept_id = p_dept
  ORDER BY cgpa DESC;
END //
DELIMITER ;

CALL GetDeptStudents(1);
CREATE TABLE marks_log(enroll_id INT,
  old_m INT, new_m INT);

CREATE TRIGGER trg_marks
AFTER UPDATE OF marks ON enrollments
BEGIN
  INSERT INTO marks_log
  VALUES (OLD.id, OLD.marks, NEW.marks);
END;

UPDATE enrollments SET marks = 99 WHERE id = 8;
SELECT * FROM marks_log;

Trade-off

Logic database-এ রাখবো?

Pros

  • Web, mobile — সব app একই rule
  • Network round-trip কম
  • Trigger audit log — লুকিয়ে marks বদলানো অসম্ভব

Cons

  • Debug, test, Git-এ রাখা কঠিন
  • DB বদলালে আবার লিখতে হয়
  • Trigger "লুকানো" কাজ করে
08

Module 08 / 08

Pro Level

Security ও Best Practice

PermissionSQL InjectionPerformanceMistakes

Security

DCL — Principle of Least Privilege

CREATE USER 'report_user'@'localhost'
  IDENTIFIED BY 'Str0ng#Pass';

GRANT SELECT ON university.*
  TO 'report_user'@'localhost';

REVOKE UPDATE ON university.enrollments
  FROM 'exam_officer'@'localhost';
RolePermission
WebsiteSELECT, INSERT, UPDATE
Analystশুধু SELECT
Exam Officerenrollments UPDATE
DBAALL
❌
Website-এ root দিয়ে connect = hacker পুরো DB DROP করতে পারবে

Security

SQL Injection — সবচেয়ে বিখ্যাত Hack

$email = $_POST['email'];
$pass  = $_POST['password'];

$sql = "SELECT * FROM users
        WHERE email = '$email'
        AND password = '$pass'";

mysqli_query($conn, $sql);

Hacker password-এ লিখলো:

' OR '1'='1

→ '1'='1' সবসময় true → login হয়ে গেল 💀

Live

নিজের চোখে দেখো

SELECT name, email FROM students
WHERE email = 'nobody' OR '1'='1';   -- সব student বের হয়ে গেল!

Fix

Prepared Statement (PDO)

$stmt = $pdo->prepare(
  "SELECT * FROM users WHERE email = ?"
);
$stmt->execute([$email]);
$user = $stmt->fetch();

if ($user && password_verify($pass, $user['password'])) {
    // login success
}
  • Input কখনো সরাসরি SQL string-এ না
  • ? placeholder → input শুধু data
  • Password: password_hash()
  • Laravel Eloquent নিজেই এটা করে

Pro Tips

Performance & Clean SQL

⚡ Performance

  • দরকারি column শুধু, SELECT * না
  • WHERE / JOIN column-এ index
  • EXPLAIN দিয়ে slow query ধরো
  • WHERE-এ column-এ function না: YEAR(date)=2024 ✗
  • Loop-এ query না (N+1 problem)

🧹 Clean Habits

  • KEYWORD বড় হাতে, নাম snake_case
  • প্রতিটা clause নতুন লাইনে
  • প্রতিটা table-এ created_at, updated_at
  • Schema বদল migration file-এ, Git-এ
  • নিয়মিত backup: mysqldump

Debug

Students-দের ৮টা Common ভুল 🐞

ভুলফলাফলসমাধান
= NULLকোনো row আসে নাIS NULL
UPDATE-এ WHERE নেইপুরো table বদলআগে SELECT, transaction
"Dhaka"column নাম ধরে নেয়'Dhaka'
WHERE COUNT(*) > 2ErrorHAVING
JOIN-এ ON নেইRow বিস্ফোরণPK = FK
LEFT JOIN + WHERE right.xINNER হয়ে যায়শর্ত ON-এ
টাকা FLOAT, phone INTPrecision হারায়DECIMAL, VARCHAR
Input string-এ জুড়ে querySQL InjectionPrepared statement
🏋️

Hands-on

Practice Time

৩–৪ জনের group · প্রথম যারা শেষ করবে → 🍫

Practice

১০টা Challenge — Easy থেকে Hard

EASY

1. Dhaka-র সব student-এর নাম ও CGPA

EASY

2. CGPA 3.5+ — বড় থেকে ছোট

EASY

3. প্রতিটা শহরে কতজন student

MEDIUM

4. Department-এর নাম সহ student (dept না থাকলেও)

MEDIUM

5. যারা কোনো course-এ enroll করেনি

MEDIUM

6. প্রতিটা course-এর গড় marks, শুধু ৭৫+

MEDIUM

7. প্রতিটা teacher ও তার head-এর নাম

HARD

8. দ্বিতীয় সর্বোচ্চ CGPA

HARD

9. Dept অনুযায়ী গড় salary (teacher না থাকলে 0)

HARD

10. প্রতিটা course-এর topper

Answer 1 / 10 · EASY

Dhaka-র সব student-এর নাম ও CGPA

SELECT name, cgpa FROM students
WHERE city = 'Dhaka';

Answer 2 / 10 · EASY

CGPA 3.5+ — বড় থেকে ছোট

SELECT name, cgpa FROM students
WHERE cgpa >= 3.5
ORDER BY cgpa DESC;

Answer 3 / 10 · EASY

প্রতিটা শহরে কতজন student

SELECT city, COUNT(*) AS total
FROM students
GROUP BY city
ORDER BY total DESC;

Answer 4 / 10 · MEDIUM

Department-এর নাম সহ student (dept না থাকলেও)

SELECT s.name, d.name AS dept
FROM students s
LEFT JOIN departments d ON d.id = s.dept_id;

Answer 5 / 10 · MEDIUM

যারা কোনো course-এ enroll করেনি

SELECT s.name FROM students s
LEFT JOIN enrollments e ON e.student_id = s.id
WHERE e.id IS NULL;

Answer 6 / 10 · MEDIUM

প্রতিটা course-এর গড় marks, শুধু ৭৫+

SELECT c.title, ROUND(AVG(e.marks),1) AS avg_marks
FROM courses c
JOIN enrollments e ON e.course_id = c.id
GROUP BY c.id, c.title
HAVING AVG(e.marks) >= 75;

Answer 7 / 10 · MEDIUM

প্রতিটা teacher ও তার head-এর নাম

SELECT t.name AS teacher,
       COALESCE(h.name, '— (নিজেই Head)') AS head
FROM teachers t
LEFT JOIN teachers h ON h.id = t.head_id;

Answer 8 / 10 · HARD

দ্বিতীয় সর্বোচ্চ CGPA

SELECT MAX(cgpa) AS second_highest
FROM students
WHERE cgpa < (SELECT MAX(cgpa) FROM students);

Answer 9 / 10 · HARD

Dept অনুযায়ী গড় salary (teacher না থাকলে 0)

SELECT d.name, COUNT(t.id) AS teachers,
       COALESCE(ROUND(AVG(t.salary)), 0) AS avg_salary
FROM departments d
LEFT JOIN teachers t ON t.dept_id = d.id
GROUP BY d.id, d.name
ORDER BY avg_salary DESC;

Answer 10 / 10 · HARD

প্রতিটা course-এর topper

WITH r AS (
  SELECT c.title, s.name, e.marks,
    RANK() OVER (PARTITION BY e.course_id ORDER BY e.marks DESC) AS rnk
  FROM enrollments e
  JOIN students s ON s.id = e.student_id
  JOIN courses  c ON c.id = e.course_id
)
SELECT title, name, marks FROM r WHERE rnk = 1;

Playground

যা খুশি লেখো, Run করো 🧪

playground.sql · Ctrl+Enter = Run

Quick Revision

Interview-এ যা আসবেই

PK vs Unique?

PK একটাই, NULL না · Unique অনেক, NULL চলে

DELETE vs TRUNCATE vs DROP?

Row মোছা / সব row দ্রুত / table সহ

WHERE vs HAVING?

Group-এর আগে row / পরে group

INNER vs LEFT JOIN?

শুধু মিল / বাম পাশের সব

RANK vs DENSE_RANK?

1, 1, 3 / 1, 1, 2

ACID?

Atomicity, Consistency, Isolation, Durability

Next Steps

এরপর কী?

🏋️

Practice

  • LeetCode — SQL 50
  • HackerRank SQL
  • SQLBolt, SQLZoo
📚

Learn

  • MySQL / PostgreSQL docs
  • "Use The Index, Luke"
  • Kaggle dataset analysis
🚀

Build

  • Library Management
  • Hostel meal system
  • PHP + MySQL Result Portal
🔥
30-Day Challenge: প্রতিদিন LeetCode-এর ১টা SQL problem, group-এ screenshot share!

Questions?

ধন্যবাদ!

"Data is the new oil" — আর SQL হলো সেই oil তোলার যন্ত্র। প্রতিদিন একটু করে practice করো।

NEmran Masud (Nayon)Instructor · Alumni
BMS BithiInstructor · Alumni

📝 Feedback

Form পূরণ করো — পরের workshop আরো ভালো হবে

🤝 Connect

LinkedIn-এ যুক্ত হও

💬 Doubt?

Group-এ যেকোনো সময় জিজ্ঞেস করো