Alumni Hands-on Workshop
Zero থেকে Confident — Real Example দিয়ে Database শেখা
Roadmap
Data → Database · Table, Key · SQL vs NoSQL · Sample DB
Data Types · CREATE · Constraints · ALTER / DROP
INSERT · SELECT · UPDATE · DELETE
WHERE · ORDER BY · Aggregate · GROUP BY
INNER · LEFT / RIGHT · SELF / CROSS · UNION
Subquery · CASE · CTE · Window
Relationship · Normalization · Index & View · Transaction
Permission · SQL Injection · Performance · Mistakes
Instructor Prep
university.sql sample DBSELECT VERSION(); চালিয়ে screenshot পাঠাও।Instructor Prep
"bKash কিভাবে জানে তোমার balance কত?"
সহজ analogy — Table = Excel sheet
Live লিখুন, ইচ্ছা করে ভুল করুন
Students ৫ মিনিট নিজে try করবে
Pros / Cons — কখন ব্যবহার করবো না
Module 01 / 08
Database-এর ভিত্তি
Foundation
কাঁচা তথ্য — আলাদাভাবে অর্থহীন
Rahim, 3.75, CSE
Process করা, অর্থবহ data
"Rahim CSE-এর top 10%-এ আছে"
সাজানো data-র সংগ্রহ — দ্রুত খোঁজা, update ও share করা যায়
Real Example
Foundation
| id 🔑 | name | cgpa | dept_id 🔗 |
|---|---|---|---|
| 1 | Rahim Uddin | 3.75 | 1 |
| 2 | Karim Hasan | 3.20 | 1 |
| 3 | Nusrat Jahan | 3.90 | 2 |
students table · dept_id → departments.id
Foundation
Database-এর সাথে কথা বলার ভাষা।
Structured Query Language · IBM, ১৯৭০-এর দশক · এখন ANSI/ISO standard · Declarative — তুমি বলো কী চাও, কিভাবে আনবে সেটা database ঠিক করে।
-- "Dhaka-র সব student-এর নাম দাও" SELECT name FROM students WHERE city = 'Dhaka';
Why SQL?
প্রতিটা transaction ও balance
Product, order, inventory
Result, registration, payment
Report, Power BI, dashboard
Foundation
| SQL (Relational) | NoSQL | |
|---|---|---|
| Structure | Fixed schema — table, column | Flexible — JSON document |
| Example | MySQL, PostgreSQL, SQL Server | MongoDB, Redis, Firebase |
| Scaling | Vertical (বড় server) | Horizontal (অনেক server) |
| Transaction | শক্তিশালী ACID | প্রায়ই eventual consistency |
| JOIN | চমৎকার | দুর্বল |
কখন কোনটা?
Banking, e-commerce order, university result — যেখানে data ভুল হলে চলবে না এবং relation অনেক।
Chat message, social feed, cache, real-time analytics — data বিশাল, structure বারবার বদলায়।
Foundation
| Database | শক্তি | দুর্বলতা | কোথায় |
|---|---|---|---|
| MySQL | Free, সহজ, XAMPP/PHP-এর সাথী | FULL JOIN নেই | WordPress |
| PostgreSQL | Feature-rich, JSON, extension | Config একটু জটিল | |
| SQLite | Server লাগে না, একটা file | বেশি concurrent write পারে না | Android, browser |
| SQL Server | Microsoft ecosystem | License খরচ | Bank, corporate |
| Oracle | Enterprise-grade reliability | অনেক দামি | Telecom, বড় bank |
Our Playground
students → departments (Many-to-One) · students ⟷ courses via enrollments (Many-to-Many) · teachers.head_id → teachers (Self)
Try it
SELECT * FROM students;
Foundation
Structure বানানো
CREATE ALTER DROP
🏗️ বাড়ির নকশা
Data বদলানো
INSERT UPDATE DELETE
🛋️ ফার্নিচার রাখা
Data পড়া
SELECT
🔍 জিনিস খোঁজা
Permission
GRANT REVOKE
🔑 চাবি দেওয়া
Transaction
COMMIT ROLLBACK
💾 Save / Undo
Module 02 / 08
Database-এর নকশা বানানো
DDL
| Type | কিসের জন্য | Example |
|---|---|---|
INT | পূর্ণ সংখ্যা | id, credit, age |
DECIMAL(10,2) | নির্ভুল দশমিক | টাকা, salary, price |
VARCHAR(n) | পরিবর্তনশীল text | name, email, phone |
CHAR(n) | নির্দিষ্ট দৈর্ঘ্য | country code 'BD' |
TEXT | বড় লেখা | comment, blog |
DATE / DATETIME | তারিখ ও সময় | dob, created_at |
BOOLEAN / ENUM | হ্যাঁ/না, নির্দিষ্ট value | is_active, status |
Pitfall #1
0.1 + 0.2 =
0.30000000000000004
bKash-এ কোটি transaction-এ পয়সার হিসাব গরমিল। টাকা = সবসময় DECIMAL
Pitfall #2
1712345678
শুরুর 0 হারিয়ে গেল!
01712345678
Phone দিয়ে অঙ্ক করি না → text
DDL
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) );
Good Habit
plural, snake_case
students course_teachers
singular, অর্থবহ
dept_id created_at
যেটাই বেছে নাও, পুরো project-এ এক রকম রাখো
DDL
ভুল data ঢোকার আগেই আটকে দেয়।
Application-এ bug থাকলেও database-এর data safe থাকে। চলো ভাঙার চেষ্টা করি ↓
Live: ভাঙার চেষ্টা
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
DDL
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 ⭐
কিছু ফার্নিচার সরানো
সব ফার্নিচার ফেলে দেওয়া
বাড়িটাই ভেঙে ফেলা
Module 03 / 08
Data ঢোকানো, পড়া, বদলানো, মোছা
CRUD · Create
-- একটা 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
LOAD DATA INFILE → ১০–১০০ গুণ দ্রুত।CRUD · Read
SELECT name AS student_name,
cgpa,
cgpa * 25 AS percentage,
'BD' AS country
FROM students;Trade-off
name, price, image লাগে — ৫০০০ অক্ষরের description আনার দরকার নেই।CRUD · Update & Delete
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
একই WHERE দিয়ে দেখো কোন row
ভুল হলে ROLLBACK
SQL_SAFE_UPDATES=1
Production-এ আগে backup
deleted_at set — আসলে মুছো না
Module 04 / 08
প্রশ্ন করার শিল্প
Querying
| Operator | মানে | Example |
|---|---|---|
= != <> | সমান / অসমান | city = 'Dhaka' |
> < >= <= | তুলনা | cgpa >= 3.5 |
AND OR NOT | শর্ত জোড়া | a AND (b OR c) |
BETWEEN | range (দুই পাশ সহ) | BETWEEN 3 AND 3.5 |
IN | list-এর যেকোনোটা | IN ('Dhaka','Sylhet') |
LIKE | pattern | name LIKE 'S%' |
IS NULL | খালি কিনা | cgpa IS NULL |
Live
-- 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
'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-এ slowThe NULL Trap
NULL ≠ 0 · NULL ≠ '' · NULL ≠ NULL
Imran নতুন ভর্তি — CGPA এখনো নেই। এটা 0 না, এটা অজানা।
The NULL Trap
-- কোনো 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
-- 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;
Real Example
SELECT id, name, price FROM products ORDER BY created_at DESC LIMIT 20 OFFSET 40; -- page 3 = (3 - 1) × 20
WHERE id < last_id (Facebook feed)Querying
| 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
🏫 সব 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 | HAVING | |
|---|---|---|
| কখন | 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
Mind-blowing 🤯
Module 05 / 08
Table জোড়া লাগানো
JOINs
দুই পাশেই মিল
A-এর সব + B-এর মিল
B-এর সব + A-এর মিল
দুই পাশের সব
JOINs
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;Danger Zone
ON ভুলে গেলে?
11 × 5 = 55 rows
Cartesian product — প্রতিটা student প্রতিটা department-এ 🤯
JOINs
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
৬ মাসে কোনো order করেনি এমন customer → discount SMS
যারা এখনো course register করেনি → reminder
আজ attendance নেই এমন employee
JOINs
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
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
SELECT name, 'Student' AS role FROM students UNION ALL SELECT name, 'Teacher' FROM teachers;
| UNION | UNION ALL |
|---|---|
| Duplicate বাদ | সব রাখে |
| Slow (compare করে) | Fast ⚡ |
Module 06 / 08
Pro-দের মতো Query
Advanced
-- সর্বোচ্চ 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
-- নিজের 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
Advanced
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;Pro Trick
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
SELECT UPPER(name), LENGTH(name) AS len, SUBSTR(email, 1, INSTR(email,'@')-1) AS user FROM students LIMIT 3;
SELECT ROUND(3.14159, 2),
ABS(-5),
10 % 3 AS modulo;SELECT NOW(), CURDATE(), DATEDIFF(NOW(), dob)/365, DATE_FORMAT(created_at, '%d %b %Y') FROM students;
Advanced
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
Advanced
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;| CGPA | ROW_NUM | RANK | DENSE |
|---|---|---|---|
| 3.9 | 1 | 1 | 1 |
| 3.9 | 2 | 1 | 1 |
| 3.7 | 3 | 3 | 2 |
Interview Classic ⭐
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)
Module 07 / 08
ভালো Database Design
Design
একজনের একটাই card। Sensitive data আলাদা রাখতে।
"Many" দিকে FK থাকে → students.dept_id
মাঝে junction table → enrollments
Junction Table
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
| student | dept | building | courses |
|---|---|---|---|
| Rahim | CSE | Block A | CSE101, CSE220, ENG101 |
| Karim | CSE | Block A | CSE101, CSE220 |
Design
প্রতিটা cell-এ একটাই value। Course list → আলাদা row-তে।
প্রতিটা column পুরো key-এর উপর নির্ভর। Course title → courses table।
Transitive dependency নেই। building নির্ভর dept-এর উপর → departments table।
"The key, the whole key, and nothing but the key."
Trade-off
Design
৫০০ পাতা উল্টে "JOIN" খোঁজা
Full Table Scan
সূচিপত্র দেখে সরাসরি পাতা ১২৩
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
WHERE a চলবে, শুধু WHERE b চলবে না।Design
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;Design
Rahim → Karim ৳১০০০
5000 → 4000
টাকা কোথায় গেল?
কখনো হলো না…
সমাধান: দুটো কাজ একসাথে — হয় দুটোই হবে, নয় কোনোটাই না।
Live
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 ⭐
হয় সব, নয় কিছুই না — অর্ধেক টাকা হারাবে না
Rule ভাঙবে না — মোট টাকা আগে-পরে সমান
একসাথে চলা transaction একে অপরকে বিরক্ত করবে না
COMMIT হলে বিদ্যুৎ গেলেও data থাকবে
Design
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
Module 08 / 08
Security ও Best Practice
Security
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';
| Role | Permission |
|---|---|
| Website | SELECT, INSERT, UPDATE |
| Analyst | শুধু SELECT |
| Exam Officer | enrollments UPDATE |
| DBA | ALL |
root দিয়ে connect = hacker পুরো DB DROP করতে পারবেSecurity
$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
$stmt = $pdo->prepare(
"SELECT * FROM users WHERE email = ?"
);
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($pass, $user['password'])) {
// login success
}? placeholder → input শুধু datapassword_hash()Pro Tips
SELECT * নাEXPLAIN দিয়ে slow query ধরোYEAR(date)=2024 ✗created_at, updated_atmysqldumpDebug
| ভুল | ফলাফল | সমাধান |
|---|---|---|
= NULL | কোনো row আসে না | IS NULL |
| UPDATE-এ WHERE নেই | পুরো table বদল | আগে SELECT, transaction |
"Dhaka" | column নাম ধরে নেয় | 'Dhaka' |
WHERE COUNT(*) > 2 | Error | HAVING |
| JOIN-এ ON নেই | Row বিস্ফোরণ | PK = FK |
| LEFT JOIN + WHERE right.x | INNER হয়ে যায় | শর্ত ON-এ |
| টাকা FLOAT, phone INT | Precision হারায় | DECIMAL, VARCHAR |
| Input string-এ জুড়ে query | SQL Injection | Prepared statement |
Hands-on
৩–৪ জনের group · প্রথম যারা শেষ করবে → 🍫
Practice
1. Dhaka-র সব student-এর নাম ও CGPA
2. CGPA 3.5+ — বড় থেকে ছোট
3. প্রতিটা শহরে কতজন student
4. Department-এর নাম সহ student (dept না থাকলেও)
5. যারা কোনো course-এ enroll করেনি
6. প্রতিটা course-এর গড় marks, শুধু ৭৫+
7. প্রতিটা teacher ও তার head-এর নাম
8. দ্বিতীয় সর্বোচ্চ CGPA
9. Dept অনুযায়ী গড় salary (teacher না থাকলে 0)
10. প্রতিটা course-এর topper
Answer 1 / 10 · EASY
SELECT name, cgpa FROM students WHERE city = 'Dhaka';
Answer 2 / 10 · EASY
SELECT name, cgpa FROM students WHERE cgpa >= 3.5 ORDER BY cgpa DESC;
Answer 3 / 10 · EASY
SELECT city, COUNT(*) AS total FROM students GROUP BY city ORDER BY total DESC;
Answer 4 / 10 · MEDIUM
SELECT s.name, d.name AS dept FROM students s LEFT JOIN departments d ON d.id = s.dept_id;
Answer 5 / 10 · MEDIUM
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
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
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
SELECT MAX(cgpa) AS second_highest FROM students WHERE cgpa < (SELECT MAX(cgpa) FROM students);
Answer 9 / 10 · HARD
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
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
Quick Revision
PK একটাই, NULL না · Unique অনেক, NULL চলে
Row মোছা / সব row দ্রুত / table সহ
Group-এর আগে row / পরে group
শুধু মিল / বাম পাশের সব
1, 1, 3 / 1, 1, 2
Atomicity, Consistency, Isolation, Durability
Next Steps
Questions?
"Data is the new oil" — আর SQL হলো সেই oil তোলার যন্ত্র। প্রতিদিন একটু করে practice করো।
Form পূরণ করো — পরের workshop আরো ভালো হবে
LinkedIn-এ যুক্ত হও
Group-এ যেকোনো সময় জিজ্ঞেস করো