সরাসরি পর্বে যান
পর্ব ৪২ · ৫৫-এর মধ্যে · মডিউল ৬

SQL — খোঁজা, সাজানো আর ছাঁকা

SQL Basics — Querying, Sorting, Filtering & Joining Data

Taught in Bangla, this lesson teaches SQL as a declarative language: DDL, DML, DQL and DCL commands, creating tables and inserting rows, SELECT with WHERE, LIKE, IN, BETWEEN and IS NULL, ORDER BY, LIMIT and DISTINCT, aggregate functions with GROUP BY and HAVING, joining two tables, safe UPDATE and DELETE with transactions, and preventing SQL injection with parameterized queries.

এই পর্বে যা শিখবেন

  • SQL কেন “কী চাই” বলার ভাষা, আর এর কমান্ডগুলোর চারটি দল
  • CREATE TABLE দিয়ে ডেটার ধরন আর নিয়মসহ টেবিল বানানো, INSERT দিয়ে ডেটা ঢোকানো
  • SELECT, WHERE, ORDER BY, LIKE, GROUP BY, HAVING আর JOIN দিয়ে প্রশ্নের উত্তর বের করা — আর ডেটাবেস কোন ক্রমে সেটা করে
  • UPDATE, DELETE আর transaction নিরাপদে ব্যবহার, আর SQL injection থেকে বাঁচার সঠিক পথ
  • HSC ICT, চাকরির পরীক্ষা আর ইন্টারভিউর SQL প্রশ্ন, আর AI-এর লেখা SQL যাচাইয়ের নিয়ম

১ · SQL কী — ডেটাবেসকে প্রশ্ন করার ভাষা

পর্ব ৪১-এ আপনি দেখেছেন, একটি রিলেশনাল ডেটাবেস ডেটা রাখে টেবিলে — সারি আর কলামে — আর key দিয়ে টেবিলগুলোকে সম্পর্কে বাঁধে। কিন্তু ডেটা জমিয়ে রাখাই শেষ কথা নয়। একটি স্কুলের প্রধান শিক্ষক জানতে চান: “রাজশাহী জেলার যে ছাত্রদের GPA ৫, তাদের নাম বর্ণানুক্রমে দাও।” হিসাবরক্ষক জানতে চান: “কোন বিষয়ের গড় নম্বর সবচেয়ে কম?” এই প্রশ্নগুলো ডেটাবেসকে বোঝানোর ভাষার নাম SQL — Structured Query Language. উচ্চারণ দুইভাবেই চলে: “এস-কিউ-এল” বা “সিকুয়েল”।

SQL-এর সবচেয়ে বড় বৈশিষ্ট্য — এটি একটি declarative language. আপনি ফলাফলের বর্ণনা দেন, পদ্ধতির নয়:

SELECT name
FROM students
WHERE district = 'Rajshahi' AND gpa = 5
ORDER BY name;

এখানে কোথাও লেখা নেই “প্রথম সারি থেকে শুরু করো, একটা একটা করে দেখো, মিললে একটি তালিকায় রাখো, শেষে তালিকা সাজাও”। পর্ব ৩৯-এর C প্রোগ্রামে এই ধাপগুলো লুপ দিয়ে নিজে লিখতে হতো। SQL-এ সেই কাজ করে ডেটাবেসের ভেতরের query optimizer — টেবিলে ৮ সারি থাকুক বা ৮০ লাখ, সে নিজেই দ্রুততম পথ বেছে নেয়।

রেস্তোরাঁয় গিয়ে আপনি বলেন, “এক প্লেট কাচ্চি, ঝাল কম।” আপনি রাঁধুনিকে বলেন না কোন হাঁড়িতে কতক্ষণ কী জ্বাল দিতে হবে — সেটা রান্নাঘরের দায়িত্ব। SQL হলো অর্ডার দেওয়ার ভাষা, আর ডেটাবেস হলো রান্নাঘর। একই অর্ডার ছোট দোকানে এক রকমভাবে, বড় হোটেলে আরেক রকমভাবে তৈরি হয় — কিন্তু আপনার অর্ডারের ভাষা বদলায় না।

আর সুখবর হলো, প্রতিদিনের কাজের বেশিরভাগই হয় হাতে গোনা কয়েকটি কমান্ডে। bKash-এর লেনদেনের ইতিহাস, বোর্ডের ফল প্রকাশের ওয়েবসাইট, ট্রেনের টিকিটের আসন, অনলাইন দোকানের অর্ডার — এসবের পেছনে কোনো না কোনো RDBMS, আর তাকে প্রশ্ন করা হয় SQL দিয়ে।

SQL কমান্ডের চারটি দল

দলকাজকমান্ড
DDL — Data Definition Languageটেবিলের কাঠামো বানানো, বদলানো, মোছাCREATE, ALTER, DROP
DML — Data Manipulation Languageটেবিলের ভেতরের ডেটা যোগ, বদল, মোছাINSERT, UPDATE, DELETE
DQL — Data Query Languageডেটা পড়া, খোঁজাSELECT
DCL — Data Control Languageকে কী করতে পারবে, সেই অনুমতিGRANT, REVOKE

অনেক পাঠ্যবইয়ে SELECT-কে DML-এর মধ্যেই ধরা হয়, আর COMMIT, ROLLBACK-কে কেউ কেউ আলাদা দল TCL (Transaction Control Language) বলেন। পরীক্ষায় যে বই অনুসরণ করছেন, তার শ্রেণিবিভাগটি মনে রাখুন — ধারণা একই।

আমাদের উদাহরণ — একটি স্কুলের ডেটাবেস

পুরো পর্ব জুড়ে একটি ছোট স্কুলের ডেটাবেস ব্যবহার করব। তিনটি টেবিল: students (রোল, নাম, জেলা, GPA), subjects (বিষয়ের কোড আর নাম), আর results (কোন রোল কোন বিষয়ে কত নম্বর পেয়েছে)। নামগুলো কাল্পনিক।

rollnamedistrictgpa
101RahimRajshahi5.00
102KarimDhaka4.50
103NusratRajshahi4.75
104FatemaKhulna5.00
105TanvirRajshahi3.80
106SadiaDhaka5.00
107ArifSylhet4.20
108MituKhulnaNULL (নতুন ভর্তি, GPA এখনো নেই)

subjects-এ তিনটি বিষয়: BAN (বাংলা), ENG (ইংরেজি), ICT. results-এ ১০১ থেকে ১০৬ রোলের প্রত্যেকের তিন বিষয়ের নম্বর — মোট ১৮টি সারি। ১০৭ আর ১০৮-এর কোনো ফল এখনো ঢোকানো হয়নি; JOIN শেখার সময় এটা কাজে লাগবে।

২ · টেবিল বানানো আর ডেটা ঢোকানো — CREATE TABLE আর INSERT

টেবিল বানানোর সময় তিনটি জিনিস ঠিক করতে হয়: কলামের নাম, প্রতিটি কলামের data type (কোন ধরনের মান থাকবে), আর constraint — এমন নিয়ম, যা ভুল ডেটা ঢুকতেই দেয় না।

CREATE TABLE students (
    roll      INTEGER PRIMARY KEY,
    name      VARCHAR(50) NOT NULL,
    district  VARCHAR(30) NOT NULL DEFAULT 'Dhaka',
    gpa       DECIMAL(3,2) CHECK (gpa BETWEEN 0 AND 5)
);

CREATE TABLE subjects (
    code   CHAR(3) PRIMARY KEY,
    title  VARCHAR(40) NOT NULL UNIQUE
);

CREATE TABLE results (
    roll     INTEGER REFERENCES students(roll),
    subject  CHAR(3) REFERENCES subjects(code),
    marks    INTEGER CHECK (marks BETWEEN 0 AND 100),
    PRIMARY KEY (roll, subject)
);
অংশমানে
INTEGER / INTপূর্ণসংখ্যা — রোল, নম্বর
DECIMAL(3,2)মোট ৩ অঙ্ক, দশমিকের পরে ২টি — যেমন 4.75. অনেক ডেটাবেসে REAL বা FLOAT-ও চলে
CHAR(3) / VARCHAR(50)লেখা। CHAR সবসময় ঠিক ৩ ঘর নেয়; VARCHAR সর্বোচ্চ ৫০, কিন্তু যতটুকু লাগে ততটুকুই
DATEতারিখ, যেমন '2026-10-19' (বছর-মাস-দিন)
PRIMARY KEYপ্রতিটি সারির অনন্য পরিচয়; ফাঁকা বা দুবার হতে পারে না। results-এ রোল আর বিষয় মিলে key — একই ছাত্রের একই বিষয়ের নম্বর দুবার ঢুকবে না
NOT NULLমান দিতেই হবে
UNIQUEএকই মান দুবার নয় — দুটি বিষয়ের একই নাম হবে না
DEFAULTমান না দিলে নিজে থেকে বসবে
CHECKশর্ত মিথ্যা হলে সারি ঢুকবে না — ১৫০ নম্বর বা GPA ৬ অসম্ভব
FOREIGN KEY (REFERENCES)results-এর roll অবশ্যই students-এ থাকতে হবে — অস্তিত্বহীন ছাত্রের ফল ঢোকানো যাবে না

পর্ব ১১-এর Excel-এর data validation-এর কথা মনে আছে? সেখানে নিয়ম ছিল পরামর্শের মতো — কপি-পেস্ট করে পাশ কাটানো যেত। ডেটাবেসের constraint আইন: কোনো পথেই নিয়ম ভাঙা ডেটা ঢোকে না, ডেটাবেস ত্রুটি দেখিয়ে কাজটি বাতিল করে।

কাঠামো বদলানো আর মোছা — ALTER আর DROP

ALTER TABLE students ADD COLUMN phone VARCHAR(15);   -- নতুন কলাম যোগ
DROP TABLE subjects;                                 -- পুরো টেবিল, ডেটাসহ, চিরতরে মুছে যায়

-- দিয়ে শুরু অংশ SQL-এর মন্তব্য (comment); ডেটাবেস সেটা পড়ে না। DROP TABLE খুব শক্তিশালী কমান্ড — বাস্তব ডেটাবেসে এটি চালানোর আগে backup আছে কি না নিশ্চিত হন।

ডেটা ঢোকানো — INSERT INTO

INSERT INTO students (roll, name, district, gpa)
VALUES (101, 'Rahim', 'Rajshahi', 5.00);

-- একসাথে কয়েকটি সারি
INSERT INTO students (roll, name, district, gpa) VALUES
    (102, 'Karim',  'Dhaka',    4.50),
    (103, 'Nusrat', 'Rajshahi', 4.75);

-- district না দিলে DEFAULT বসবে: 'Dhaka'
INSERT INTO students (roll, name, gpa) VALUES (109, 'Joy', 4.00);

INSERT INTO-তে কলামের নাম লিখে দেওয়া ভালো অভ্যাস — তাহলে পরে টেবিলে নতুন কলাম যোগ হলেও কমান্ডটি ভাঙে না, আর কোন মান কোন কলামে যাচ্ছে তা পড়েই বোঝা যায়। লেখা আর তারিখ থাকে একক উদ্ধৃতিচিহ্নে ('Rajshahi'), সংখ্যা উদ্ধৃতিচিহ্ন ছাড়া। এখন যদি কেউ (110, 'Rana', 'Bogura', 6.5) ঢোকাতে চান, CHECK নিয়মের কারণে ডেটাবেস তা প্রত্যাখ্যান করবে।

৩ · SELECT আর WHERE — যা চাই, শুধু সেটুকু

SELECT হলো SQL-এর সবচেয়ে বেশি ব্যবহৃত কমান্ড। সবচেয়ে সহজ রূপ:

SELECT * FROM students;                      -- সব কলাম, সব সারি
SELECT name, gpa FROM students;              -- শুধু দুটি কলাম
SELECT name AS student_name, gpa * 20 AS percent
FROM students;                               -- নতুন নাম আর হিসাব করা কলাম

* মানে “সব কলাম” — দ্রুত দেখে নেওয়ার জন্য সুবিধাজনক, কিন্তু প্রোগ্রামে শুধু দরকারি কলামগুলো নাম ধরে চাওয়াই ভালো। AS দিয়ে ফলাফলের কলামকে নতুন নাম দেওয়া যায়; একে বলে alias. SQL-এর কীওয়ার্ড (SELECT, FROM) বড় হাতের বা ছোট হাতের — দুটোই চলে; বড় হাতে লেখা শুধু পড়ার সুবিধার জন্য প্রচলিত রীতি। প্রতিটি বাক্যের শেষে ; দিন।

WHERE — সারি ছাঁকা

WHERE একটি শর্ত নেয়; যে সারিতে শর্ত সত্য, শুধু সেগুলোই থাকে। পর্ব ১৯-এর বুলিয়ান অ্যালজেব্রা এখানে সরাসরি কাজে লাগে — AND, OR আর NOT ঠিক সেভাবেই কাজ করে।

অপারেটরউদাহরণফল (আমাদের টেবিলে)
= <> < > <= >=WHERE gpa >= 4.75Rahim, Nusrat, Fatema, Sadia (<> মানে “সমান নয়”; অনেক ডেটাবেসে !=-ও চলে)
ANDWHERE district = 'Dhaka' AND gpa = 5Sadia
ORWHERE district = 'Khulna' OR district = 'Sylhet'Fatema, Arif, Mitu
NOTWHERE NOT district = 'Rajshahi'রাজশাহীর বাইরের ৫ জন
BETWEEN … AND …WHERE gpa BETWEEN 4 AND 4.8Karim, Nusrat, Arif (দুই সীমাও অন্তর্ভুক্ত)
IN (…)WHERE district IN ('Dhaka', 'Khulna')Karim, Fatema, Sadia, Mitu
LIKEWHERE name LIKE 'S%'Sadia
IS NULL / IS NOT NULLWHERE gpa IS NULLMitu

AND আর OR একসাথে থাকলে AND আগে হিসাব হয় — যেমন গণিতে গুণ যোগের আগে। সন্দেহ থাকলে বন্ধনী দিন: WHERE (district = 'Dhaka' OR district = 'Khulna') AND gpa = 5 দেয় Fatema আর Sadia.

LIKE — ধাঁচ মিলিয়ে খোঁজা

LIKE-এ দুটি বিশেষ চিহ্ন (wildcard): % মানে “যেকোনো সংখ্যক অক্ষর, শূন্যও হতে পারে”, আর _ মানে “ঠিক একটি অক্ষর”।

  • name LIKE 'S%' — S দিয়ে শুরু: Sadia.
  • name LIKE '%a' — a দিয়ে শেষ: Fatema, Sadia.
  • name LIKE '%im%' — মাঝে কোথাও “im”: Rahim, Karim.
  • name LIKE '_i%' — দ্বিতীয় অক্ষর i: Mitu.

বড়-ছোট হাতের অক্ষর নিয়ে সাবধান: SQLite আর MySQL-এ সাধারণত LIKE-এর কাছে 's' আর 'S' এক, কিন্তু PostgreSQL-এ আলাদা (সেখানে বড়-ছোট না মানতে ILIKE). Microsoft Access-এ এর বদলে * আর ? চিহ্ন ব্যবহৃত হয়।

NULL — শূন্য নয়, “অজানা”

Mitu-র GPA-র ঘরে NULL — মানে GPA এখনো জানা নেই। এটি ০ নয়, ফাঁকা লেখাও নয়। NULL-এর সাথে যেকোনো তুলনার ফল “সত্য” বা “মিথ্যা” নয়, “অজানা” — তাই WHERE gpa = NULL কোনো সারিই দেয় না! NULL খুঁজতে সবসময় IS NULL লিখুন। ভাবনার প্রশ্ন ৪-এ এর পেছনের গল্প আছে।

ডেটাবেস প্রশ্নটি কোন ক্রমে প্রক্রিয়া করে

আমরা লিখি SELECT দিয়ে শুরু করে, কিন্তু ডেটাবেস কাজ শুরু করে FROM থেকে। নিচের প্রশ্নটি ধাপে ধাপে দেখুন — প্রতি ধাপে টেবিল কীভাবে ছোট হয়, লক্ষ করুন:

ধাপ ১ · FROM students৮টি সারি
 roll  name    district  gpa
 101   Rahim   Rajshahi  5.00
 102   Karim   Dhaka     4.50
 103   Nusrat  Rajshahi  4.75
 104   Fatema  Khulna    5.00
 105   Tanvir  Rajshahi  3.80
 106   Sadia   Dhaka     5.00
 107   Arif    Sylhet    4.20
 108   Mitu    Khulna    NULL
ধাপ ২ · WHERE district = 'Rajshahi'৮ → ৩টি সারি
 roll  name    district  gpa
 101   Rahim   Rajshahi  5.00   ✔
 102   Karim   Dhaka     4.50   ✘
 103   Nusrat  Rajshahi  4.75   ✔
 104   Fatema  Khulna    5.00   ✘
 105   Tanvir  Rajshahi  3.80   ✔
 106   Sadia   Dhaka     5.00   ✘
 107   Arif    Sylhet    4.20   ✘
 108   Mitu    Khulna    NULL   ✘
ধাপ ৩ · ORDER BY gpa DESCবড় থেকে ছোট
 roll  name    district  gpa
 101   Rahim   Rajshahi  5.00
 103   Nusrat  Rajshahi  4.75
 105   Tanvir  Rajshahi  3.80
ধাপ ৪ · SELECT nameশুধু চাওয়া কলাম
 roll  name    district  gpa
 101   Rahim   Rajshahi  5.00
 103   Nusrat  Rajshahi  4.75
 105   Tanvir  Rajshahi  3.80
ফলাফল৩টি সারি, ১টি কলাম
 name
 Rahim
 Nusrat
 Tanvir

 লেখার ক্রম:  SELECT → FROM → WHERE → ORDER BY
 যৌক্তিক ক্রম: FROM → WHERE → SELECT → ORDER BY
  1. FROM আগে। ডেটাবেস প্রথমে জানতে চায় — কোন টেবিল? students-এর আটটি সারিই এখন হাতে। কোন কলাম দেখাতে হবে, তা এখনো প্রশ্ন নয়।
  2. WHERE ছাঁকে। প্রতিটি সারিতে শর্ত পরীক্ষা: district কি 'Rajshahi'? তিনটি সারি টিকে থাকে, পাঁচটি বাদ। বড় টেবিলে ডেটাবেস এখানে index ব্যবহার করে সব সারি না পড়েই মিলগুলো খুঁজে নিতে পারে।
  3. ORDER BY সাজায়। টিকে থাকা তিনটি সারি gpa অনুযায়ী বড় থেকে ছোট (DESC). লক্ষ করুন, gpa ফলাফলে দেখানো হবে না — তবু তা দিয়ে সাজানো যায়, কারণ এই মুহূর্তে পুরো সারি হাতে আছে।
  4. SELECT কলাম বাছে। এবার শুধু name কলাম রাখা হয়; বাকি কলাম ফেলে দেওয়া হয়।
  5. ফলাফল। Rahim, Nusrat, Tanvir. মনে রাখার কৌশল: আগে টেবিল, তারপর সারি ছাঁকা, শেষে কলাম বাছাই আর সাজানো। এখানে বোঝার সুবিধার জন্য সাজানো আগে দেখানো হয়েছে; SQL-এর যৌক্তিক ক্রমে আসলে SELECT আগে, ORDER BY পরে — পুরো ক্রম: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT (তবু ORDER BY ফলাফলে না দেখানো কলাম দিয়েও সাজাতে পারে)। এজন্যই ORDER BY-তে SELECT-এর দেওয়া alias ব্যবহার করা যায়, কিন্তু WHERE-এ যায় না।

৪ · সাজানো, সীমা আর পুনরাবৃত্তি বাদ — ORDER BY, LIMIT, DISTINCT

ORDER BY ফলাফল সাজায়। ASC (ascending) মানে ছোট থেকে বড় — সংখ্যায় ১, ২, ৩; লেখায় A থেকে Z. DESC (descending) উল্টো। কিছু না লিখলে ডিফল্ট ASC — পরীক্ষায় এই প্রশ্নটি খুব আসে।

SELECT name, gpa FROM students
ORDER BY gpa DESC, name ASC;     -- GPA বড় থেকে ছোট; সমান হলে নাম অনুযায়ী

SELECT name, gpa FROM students
WHERE gpa IS NOT NULL
ORDER BY gpa DESC
LIMIT 3;                         -- সেরা তিনজন

SELECT DISTINCT district FROM students
ORDER BY district;               -- প্রতিটি জেলা একবার

প্রত্যাশিত আউটপুট (তৃতীয় প্রশ্ন):

district
Dhaka
Khulna
Rajshahi
Sylhet
  • একাধিক কলামে সাজানো: প্রথম কলামে সমান হলে দ্বিতীয়টি দেখা হয়। Rahim, Fatema আর Sadia — তিনজনেরই GPA ৫; তাই নাম অনুযায়ী Fatema, Rahim, Sadia.
  • LIMIT: প্রথম কয়টি সারি। ORDER BY ছাড়া LIMIT দিলে “কোন তিনটি” তা নিশ্চিত নয় — ডেটাবেস যেকোনো ক্রমে দিতে পারে। এখানে ৫.০০-এ তিনজন সমান, তাই সেরা তিনজন ঠিক এরাই। SQL Server আর Access-এ লেখা হয় SELECT TOP 3 ….
  • DISTINCT: পুনরাবৃত্তি বাদ। আট সারিতে চারটি জেলা।
  • NULL কোথায় বসে: SQLite আর MySQL-এ ASC সাজালে NULL সবার আগে আসে; PostgreSQL আর Oracle-এ সবার শেষে। এজন্যই ওপরে WHERE gpa IS NOT NULL দিয়ে Mitu-কে আগেই বাদ দেওয়া হয়েছে।
মূল কথা

ডেটাবেসের টেবিলে সারির কোনো নিশ্চিত ক্রম নেই। যে ফলাফলে ক্রম গুরুত্বপূর্ণ — মেধাতালিকা, রিপোর্ট, প্রথম ১০টি — সেখানে সবসময় ORDER BY লিখুন। আজ সঠিক ক্রমে আসছে বলে কাল আসবে, এমন নিশ্চয়তা নেই।

৫ · হিসাব করা — aggregate function, GROUP BY আর HAVING

অনেক সারি থেকে একটি সংখ্যা বের করার ফাংশনকে বলে aggregate function:

ফাংশনপ্রশ্নউত্তর
COUNT(*)SELECT COUNT(*) FROM students;8 (সব সারি)
COUNT(gpa)SELECT COUNT(gpa) FROM students;7 (NULL গোনা হয় না)
SUMSELECT SUM(marks) FROM results WHERE roll = 101;255
AVGSELECT AVG(marks) FROM results WHERE subject = 'ICT';84.67 (প্রায়)
MIN / MAXSELECT MIN(marks), MAX(marks) FROM results;48, 95

COUNT(*) আর COUNT(gpa)-এর পার্থক্যটি লক্ষ করুন: aggregate function NULL মান উপেক্ষা করে। AVG(gpa)-ও Mitu-কে বাদ দিয়ে বাকি সাতজনের গড় করবে — Mitu-কে শূন্য ধরে আটজনে ভাগ করবে না।

GROUP BY — দলে দলে হিসাব

“প্রতিটি বিষয়ের গড় নম্বর” — একটি গড় নয়, বিষয়প্রতি একটি করে। GROUP BY একই মানের সারিগুলোকে এক দলে জড়ো করে, তারপর aggregate function প্রতিটি দলে আলাদাভাবে চলে। পর্ব ১১-এর Pivot Table মনে পড়ছে? একই ধারণা। আর দল তৈরি হওয়ার পরে দলগুলোকে ছাঁকতে লাগে HAVING:

results টেবিল১৮টি সারি
 roll  subject  marks      roll  subject  marks
 101   BAN      78         104   BAN      90
 101   ENG      85         104   ENG      88
 101   ICT      92         104   ICT      79
 102   BAN      65         105   BAN      55
 102   ENG      70         105   ENG      48
 102   ICT      88         105   ICT      70
 103   BAN      82         106   BAN      72
 103   ENG      74         106   ENG      91
 103   ICT      95         106   ICT      84
GROUP BY subject৩টি ঝুড়ি
 BAN │ 78  65  82  90  55  72
 ENG │ 85  70  74  88  48  91
 ICT │ 92  88  95  79  70  84
AVG(marks) প্রতি ঝুড়িতেযোগফল ÷ ৬
 BAN │ 442 ÷ 6 = 73.67
 ENG │ 456 ÷ 6 = 76.00
 ICT │ 508 ÷ 6 = 84.67
HAVING AVG(marks) > 75দল ছাঁকা
 subject  avg
 BAN      73.67   ✘  (৭৫-এর কম)
 ENG      76.00   ✔
 ICT      84.67   ✔
  1. কাঁচা ডেটা। ছয়জন ছাত্র × তিনটি বিষয় = ১৮টি সারি। এখান থেকে বিষয়প্রতি একটি গড় চাই।
  2. ঝুড়ি তৈরি। GROUP BY subject প্রতিটি আলাদা বিষয়ের জন্য একটি ঝুড়ি বানায়, আর প্রতিটি সারিকে তার বিষয়ের ঝুড়িতে ফেলে। ১৮টি সারি এখন তিনটি দলে। পরীক্ষার খাতা বিষয় অনুযায়ী তিনটি স্তূপে ভাগ করার মতো।
  3. প্রতিটি ঝুড়িতে হিসাব। AVG প্রতিটি দলে আলাদাভাবে চলে, আর প্রতিটি দল থেকে একটি করে সারি বের হয়। এখন থেকে SELECT-এ শুধু দুই ধরনের কলাম রাখা যায় — যা দিয়ে দল বানানো হয়েছে (subject), আর aggregate (AVG). রোল লিখলে প্রশ্ন ওঠে “কোন রোল?” — একটি দলে ছয়টি রোল আছে।
  4. HAVING দল ছাঁকে। গড় ৭৫-এর বেশি এমন বিষয় — ENG আর ICT. WHERE এখানে চলত না, কারণ WHERE কাজ করে দল তৈরির আগে, একেকটি সারির ওপর; তখনো কোনো গড়ই হিসাব হয়নি। নিয়ম: WHERE ছাঁকে সারি, HAVING ছাঁকে দল।

এবার পর্যন্ত যা শিখলেন, তা নিজে চালিয়ে দেখুন। নিচের ব্লকে আসল বিষয় SQL-এর লাইনগুলো — Python এখানে শুধু বাহক: সে মেমরিতে (":memory:") একটি অস্থায়ী SQLite ডেটাবেস বানায়, আমাদের দুটি টেবিল তৈরি করে, ডেটা ঢোকায়, তারপর পাঁচটি SELECT চালিয়ে ফল ছাপে। Python না জানলেও চলবে — ত্রিপল উদ্ধৃতিচিহ্নের (""") ভেতরের আর show(...)-এর দ্বিতীয় অংশের SQL পড়ুন। SQL বদলে আবার চালিয়ে দেখুন।

import sqlite3
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE students (roll INTEGER PRIMARY KEY, name TEXT NOT NULL,
  district TEXT NOT NULL, gpa REAL CHECK (gpa BETWEEN 0 AND 5));
CREATE TABLE results (roll INTEGER REFERENCES students(roll), subject TEXT,
  marks INTEGER CHECK (marks BETWEEN 0 AND 100), PRIMARY KEY (roll, subject));
INSERT INTO students VALUES (101,'Rahim','Rajshahi',5.0), (102,'Karim','Dhaka',4.5),
  (103,'Nusrat','Rajshahi',4.75), (104,'Fatema','Khulna',5.0), (105,'Tanvir','Rajshahi',3.8),
  (106,'Sadia','Dhaka',5.0), (107,'Arif','Sylhet',4.2), (108,'Mitu','Khulna',NULL);
INSERT INTO results VALUES (101,'BAN',78),(101,'ENG',85),(101,'ICT',92),(102,'BAN',65),
  (102,'ENG',70),(102,'ICT',88),(103,'BAN',82),(103,'ENG',74),(103,'ICT',95),(104,'BAN',90),
  (104,'ENG',88),(104,'ICT',79),(105,'BAN',55),(105,'ENG',48),(105,'ICT',70),(106,'BAN',72),
  (106,'ENG',91),(106,'ICT',84);""")
def show(title, sql):
    print("--", title)
    for row in db.execute(sql): print(*row, sep=" | ")
show("WHERE + ORDER BY", "SELECT name, gpa FROM students WHERE district = 'Rajshahi' ORDER BY gpa DESC")
show("LIKE '%a'", "SELECT roll, name FROM students WHERE name LIKE '%a' ORDER BY roll")
show("IS NULL", "SELECT name FROM students WHERE gpa IS NULL")
show("COUNT per district", "SELECT district, COUNT(*) FROM students GROUP BY district ORDER BY district")
show("AVG per subject", "SELECT subject, ROUND(AVG(marks), 2) FROM results GROUP BY subject ORDER BY subject")

আউটপুটের প্রথম অংশ Rahim | 5.0, Nusrat | 4.75, Tanvir | 3.8 — ঠিক প্রথম stepper-এর ফল (Python দশমিকের শেষের শূন্য দেখায় না)। LIKE দেয় 104 | Fatema আর 106 | Sadia; IS NULL দেয় Mitu. জেলাভিত্তিক গণনা: Dhaka | 2, Khulna | 2, Rajshahi | 3, Sylhet | 1. শেষে বিষয়ভিত্তিক গড়: BAN | 73.67, ENG | 76.0, ICT | 84.67 — দ্বিতীয় stepper-এর সাথে মিলিয়ে নিন। প্রতিটি প্রশ্নে ORDER BY থাকায় ফল প্রতিবার একই ক্রমে আসে। চেষ্টা করুন: শেষ প্রশ্নে HAVING AVG(marks) > 75 যোগ করুন (GROUP BY-এর পরে, ORDER BY-এর আগে), বা একটি INSERT-এ marks 150 দিয়ে দেখুন CHECK কী ত্রুটি দেখায়।

৬ · JOIN — দুই টেবিল মেলানো

পর্ব ৪১-এ দেখেছেন, normalization-এর জন্য ছাত্রের নাম আর ফলাফল আলাদা টেবিলে রাখা হয় — results-এ শুধু রোল। কিন্তু রিপোর্টে তো নাম চাই! JOIN দুই টেবিলের সারিগুলোকে একটি মিলের শর্তে (এখানে রোল) পাশাপাশি জুড়ে দেয়। ভাবুন, ভর্তির রেজিস্টার খাতা আর নম্বরের খাতা পাশাপাশি রেখে রোল মিলিয়ে নাম বসানো — Excel-এর XLOOKUP যা করত, তাই।

SELECT s.name, r.marks
FROM students AS s
INNER JOIN results AS r ON s.roll = r.roll
WHERE r.subject = 'ICT'
ORDER BY r.marks DESC;

students AS s — টেবিলের ছোট নাম (alias), যাতে বারবার পুরো নাম লিখতে না হয়। s.roll মানে students-এর roll; দুই টেবিলেই roll আছে, তাই কোনটি তা বলে দিতে হয়। ON-এর পরে মিলের শর্ত।

দুই টেবিলresults: শুধু ICT
 students (s)          results (r)
 roll  name            roll  marks
 101   Rahim           101   92
 102   Karim           102   88
 103   Nusrat          103   95
 104   Fatema          104   79
 105   Tanvir          105   70
 106   Sadia           106   84
 107   Arif
 108   Mitu
ON s.roll = r.rollমিল খোঁজা
 101 Rahim   ═══ 101  92
 102 Karim   ═══ 102  88
 103 Nusrat  ═══ 103  95
 104 Fatema  ═══ 104  79
 105 Tanvir  ═══ 105  70
 106 Sadia   ═══ 106  84
 107 Arif    ─── ?  (মিল নেই)
 108 Mitu    ─── ?  (মিল নেই)
INNER JOIN-এর ফলORDER BY marks DESC
 name    marks
 Nusrat  95
 Rahim   92
 Karim   88
 Sadia   84
 Fatema  79
 Tanvir  70
 (Arif আর Mitu নেই — মিল না পাওয়া সারি বাদ)
LEFT JOIN-এর ফলবাঁয়ের সবাই থাকে
 name    marks
 Rahim   92
 ...     ...
 Sadia   84
 Arif    NULL
 Mitu    NULL

 + WHERE r.roll IS NULL  →  Arif, Mitu
   (যাদের ফল এখনো ঢোকানো হয়নি)
  1. দুটি খাতা। বাঁয়ে আটজন ছাত্র, ডানে ICT-র ছয়টি নম্বর। নাম আছে এক টেবিলে, নম্বর অন্যটিতে — যোগসূত্র শুধু রোল।
  2. মিল খোঁজা। ON s.roll = r.roll: প্রতিটি ছাত্রের রোলের সাথে নম্বরের খাতার রোল মেলানো হয়। ছয়জনের জোড়া পাওয়া গেল; Arif আর Mitu-র কোনো জোড়া নেই।
  3. INNER JOIN. INNER JOIN শুধু জোড়া পাওয়া সারিগুলো রাখে — দুই টেবিলেই যাদের অস্তিত্ব আছে। শুধু JOIN লিখলেও INNER JOIN বোঝায়।
  4. LEFT JOIN. LEFT JOIN বাঁ দিকের টেবিলের (students) সবাইকে রাখে; জোড়া না পেলে ডানের কলামে NULL বসায়। এর সাথে WHERE r.roll IS NULL দিলে ঠিক তারাই আসে, যাদের ফল নেই — “কার নম্বর এখনো জমা হয়নি?” প্রশ্নের উত্তর।

ভুল JOIN-এ সংখ্যা ফুলে ওঠে: ICT-র শর্ত (WHERE r.subject = 'ICT') বাদ দিলে প্রতিটি ছাত্রের তিনটি করে সারি আসবে — ১৮টি। তখন SUM(s.gpa) করলে প্রতিটি GPA তিনবার যোগ হবে, নীরবে। আর ON-এর শর্তই ভুলে গেলে প্রতিটি ছাত্র প্রতিটি নম্বরের সাথে জুড়ে যায় — ৮ × ১৮ = ১৪৪টি অর্থহীন সারি। JOIN-এর পরে সারির সংখ্যা যা আশা করেছিলেন, তার সাথে মিলিয়ে দেখার অভ্যাস করুন।

৭ · ডেটা বদলানো আর মোছা — UPDATE, DELETE আর transaction

-- খাতা পুনর্নিরীক্ষায় Tanvir-এর ICT নম্বর ৭০ থেকে ৭৫
UPDATE results
SET marks = 75
WHERE roll = 105 AND subject = 'ICT';

-- ভুল করে ঢোকানো একটি সারি মোছা
DELETE FROM results
WHERE roll = 105 AND subject = 'ENG';

UPDATE-এর গঠন: কোন টেবিল, SET দিয়ে কোন কলামে কী মান, আর WHERE দিয়ে কোন সারিগুলোতে। DELETE-এ শুধু টেবিল আর WHERE. দুটিরই সবচেয়ে বিপজ্জনক অংশ — WHERE ঐচ্ছিক। না লিখলে ডেটাবেস ধরে নেয় “সব সারি”, আর কোনো প্রশ্ন না করে কাজটা করে ফেলে।

যা চেয়েছিলেন১টি সারি মোছা
DELETE FROM results
WHERE roll = 105 AND subject = 'ENG';

 105  BAN  55
 105  ENG  48   ← মুছে গেল
 105  ICT  70
 ... বাকি ১৭টি সারি অক্ষত

 ফল: 1 row deleted
WHERE ভুলে গেলেদুর্ঘটনা
DELETE FROM results;      ← WHERE নেই!

 ১৮টি সারির সবগুলো মুছে গেল
 টেবিল আছে, কাঠামো আছে — ডেটা নেই

 ফল: 18 rows deleted
 (কোনো সতর্কবার্তা নেই)
নিরাপদ অভ্যাসআগে SELECT
SELECT COUNT(*) FROM results
WHERE roll = 105 AND subject = 'ENG';
 → 1       ✔ ঠিক যা আশা করেছি

এবার একই WHERE দিয়ে:
DELETE FROM results
WHERE roll = 105 AND subject = 'ENG';
transaction — শেষ সুরক্ষাBEGIN … ROLLBACK
BEGIN;
DELETE FROM results;          → 18 rows  (১-এর বদলে!)
ROLLBACK;                     → সব ফিরে এলো

BEGIN;
DELETE FROM results WHERE roll = 105 AND subject = 'ENG';
                              → 1 row
COMMIT;                       → স্থায়ী হলো
  1. যা চেয়েছিলেন। Tanvir-এর ইংরেজির একটি ভুল সারি মুছতে হবে। WHERE-এর দুটি শর্ত মিলে ঠিক একটি সারি চিহ্নিত করে — রোল আর বিষয় মিলেই তো primary key.
  2. WHERE ভুলে গেলে। তাড়াহুড়োয় প্রথম লাইন লিখেই Enter চাপলেন। ডেটাবেস “আপনি কি নিশ্চিত?” জিজ্ঞেস করে না — ১৮টি সারিই মুছে যায়। DROP TABLE-এর মতো টেবিল হারায় না, কিন্তু ডেটা শেষ। UPDATE-এ WHERE ভুললে সব ছাত্রের নম্বর একসাথে ৭৫ হয়ে যেত।
  3. আগে SELECT. পেশাদারদের অভ্যাস: যে WHERE দিয়ে বদলাবেন বা মুছবেন, আগে সেটাই দিয়ে SELECT চালিয়ে দেখুন কতগুলো আর কোন সারি আসে। মিললে তারপর DELETE বা UPDATE.
  4. transaction. BEGIN দিয়ে শুরু করলে বদলগুলো সাময়িক থাকে। ফলাফলের সারিসংখ্যা অপ্রত্যাশিত হলে ROLLBACK — সব আগের মতো। ঠিক থাকলে COMMIT — স্থায়ী। (কিছু টুল নিজে নিজে প্রতিটি বাক্য commit করে, তখন এই সুরক্ষা থাকে না; তাই backup-ও জরুরি।)

Transaction — হয় সব, নয় কিছুই না

bKash-এ আপনি ৫০০ টাকা পাঠালেন। ডেটাবেসে দুটি কাজ: আপনার হিসাব থেকে ৫০০ কাটা, প্রাপকের হিসাবে ৫০০ জমা। প্রথমটির পরে যদি বিদ্যুৎ চলে যায়? টাকা কাটা হলো, কিন্তু কোথাও জমা হলো না! transaction এই দুটি কাজকে একটি অবিভাজ্য গুচ্ছ বানায়: BEGIN → দুটি UPDATE → COMMIT. মাঝপথে কিছু ভুল হলে ROLLBACK, আর ডেটাবেস নিশ্চিত করে — হয় দুটিই ঘটবে, নয়তো একটিও না। COMMIT হওয়ার পরে বিদ্যুৎ গেলেও বদল হারায় না, আর ROLLBACK সব বাতিল করে। এই নিশ্চয়তাগুলোর সংক্ষিপ্ত নাম ACID (Atomicity, Consistency, Isolation, Durability) — ব্যাংক আর মোবাইল ব্যাংকিং ডেটাবেসের ভিত্তি।

দ্বিতীয় চালানোর মতো উদাহরণে JOIN, “WHERE ভুলে যাওয়া DELETE”-কে ROLLBACK দিয়ে বাঁচানো, UPDATE-এর পরে COMMIT, আর ব্যবহারকারীর দেওয়া মান নিরাপদে পাঠানো — সব একসাথে। আবারও, Python শুধু বাহক; ত্রিপল উদ্ধৃতিচিহ্ন আর execute("...")-এর ভেতরের SQL-ই আসল:

import sqlite3
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE students (roll INTEGER PRIMARY KEY, name TEXT, district TEXT, gpa REAL);
CREATE TABLE results (roll INTEGER REFERENCES students(roll), subject TEXT, marks INTEGER);
INSERT INTO students VALUES (101,'Rahim','Rajshahi',5.0), (102,'Karim','Dhaka',4.5),
  (103,'Nusrat','Rajshahi',4.75), (104,'Fatema','Khulna',5.0), (105,'Tanvir','Rajshahi',3.8),
  (106,'Sadia','Dhaka',5.0), (107,'Arif','Sylhet',4.2), (108,'Mitu','Khulna',NULL);
INSERT INTO results VALUES (101,'ICT',92), (102,'ICT',88), (103,'ICT',95),
  (104,'ICT',79), (105,'ICT',70), (106,'ICT',84);""")
print("INNER JOIN:", db.execute("""SELECT s.name, r.marks FROM students AS s
  INNER JOIN results AS r ON s.roll = r.roll ORDER BY r.marks DESC LIMIT 3""").fetchall())
print("LEFT JOIN, no result:", db.execute("""SELECT s.name, r.marks FROM students AS s
  LEFT JOIN results AS r ON s.roll = r.roll WHERE r.roll IS NULL ORDER BY s.roll""").fetchall())
cur = db.execute("DELETE FROM results")          # WHERE forgotten!
print("deleted", cur.rowcount, "rows -> ROLLBACK")
db.rollback()                                    # undo the whole transaction
db.execute("UPDATE results SET marks = 75 WHERE roll = 105 AND subject = 'ICT'")
db.commit()                                      # make it permanent
print(db.execute("""SELECT s.name, r.marks FROM students AS s JOIN results AS r
  ON s.roll = r.roll WHERE s.roll = ?""", (105,)).fetchone())  # 105: from a web form
print("rows in results:", db.execute("SELECT COUNT(*) FROM results").fetchone()[0])

প্রথম লাইন INNER JOIN: [('Nusrat', 95), ('Rahim', 92), ('Karim', 88)] — ICT-র সেরা তিনজন (LIMIT 3). দ্বিতীয় লাইন LEFT JOIN, no result: [('Arif', None), ('Mitu', None)] — Python-এ SQL-এর NULL দেখায় None. তারপর deleted 6 rows -> ROLLBACK: WHERE ছাড়া DELETE সব ছয়টি সারি মুছেছিল, কিন্তু Python-এর sqlite3 নিজে থেকেই DELETE-এর আগে একটি transaction খুলেছিল (BEGIN), তাই rollback() সব ফিরিয়ে আনে। এরপর UPDATE আর commit(), আর ('Tanvir', 75) দেখায় বদলটি হয়েছে। শেষ লাইন rows in results: 6 — একটিও সারি হারায়নি। লক্ষ করুন WHERE s.roll = ?: রোলের মান SQL-এর লেখার সাথে জোড়া দেওয়া হয়নি, ? চিহ্নের জায়গায় আলাদা করে পাঠানো হয়েছে। কেন, তা পরের অংশে।

View আর index — এক লাইনে

  • View: একটি SELECT-কে নাম দিয়ে সংরক্ষণ — CREATE VIEW ict_marks AS SELECT …; তারপর SELECT * FROM ict_marks; যেন একটি টেবিল। জটিল প্রশ্ন বারবার না লিখতে, আর কাউকে টেবিলের শুধু কিছু কলাম দেখাতে কাজে লাগে।
  • Index: CREATE INDEX idx_district ON students(district); — বইয়ের শেষের নির্ঘণ্টের মতো, যাতে জেলা দিয়ে খুঁজতে পুরো টেবিল পড়তে না হয়। পড়া দ্রুত হয়, কিন্তু প্রতিটি INSERT/UPDATE-এ index-ও হালনাগাদ করতে হয় বলে লেখা একটু ধীর হয়।

৮ · নানা ডেটাবেসের SQL, নিরাপত্তা আর অনুশীলনের জায়গা

একই ভাষা, নানা উচ্চারণ — SQL dialect

SQL-এর একটি আন্তর্জাতিক মান (ANSI/ISO standard) আছে, কিন্তু প্রতিটি ডেটাবেস সফটওয়্যার তার সাথে কিছু নিজস্ব সংযোজন করে — একে বলে SQL dialect. ভালো খবর: এই পর্বের প্রায় সবকিছু সবখানে একইভাবে চলে।

ডেটাবেসকোথায় দেখা যায়ছোট পার্থক্যের উদাহরণ
MySQL / MariaDBওয়েবসাইট, WordPress, cPanel হোস্টিংLIMIT 3; AUTO_INCREMENT
PostgreSQLবড় ওয়েব অ্যাপ, সরকারি-বেসরকারি সিস্টেমLIKE বড়-ছোট হাতের অক্ষর মানে; ILIKE
SQLiteমোবাইল অ্যাপ, ব্রাউজার, ছোট প্রোগ্রাম — সার্ভার লাগে নাডেটার ধরন নমনীয়; foreign key চালু করতে আলাদা সেটিং
Microsoft SQL Serverব্যাংক, কর্পোরেট অফিসSELECT TOP 3 …
Oracleবড় ব্যাংক, টেলিকমFETCH FIRST 3 ROWS ONLY
Microsoft Accessছোট অফিসের ডেস্কটপ ডেটাবেসLIKE-এ * আর ?; ক্লিক করে query বানানোর ডিজাইন ভিউ

SQL injection — কেন ব্যবহারকারীর লেখা কখনো SQL-এর সাথে জোড়া দেবেন না

ধরুন একটি ওয়েবসাইটে ছাত্র রোল লিখে ফল দেখেন। প্রোগ্রামার যদি ব্যবহারকারীর লেখা সরাসরি SQL বাক্যের লেখার সাথে জুড়ে দেন (string concatenation), তাহলে ডেটাবেস বুঝতে পারে না কোন অংশ প্রোগ্রামারের কমান্ড আর কোন অংশ ব্যবহারকারীর ডেটা। কেউ রোলের ঘরে সংখ্যার বদলে SQL-এর অংশের মতো দেখতে কিছু লিখলে সেটাও কমান্ড হিসেবে চলে যেতে পারে — অন্যের তথ্য দেখা, এমনকি টেবিল মুছে ফেলা পর্যন্ত। এই দুর্বলতার নাম SQL injection, আর এটি দুই দশকের বেশি সময় ধরে ওয়েবের সবচেয়ে ক্ষতিকর ত্রুটিগুলোর একটি।

সমাধান সহজ আর একটাই: parameterized query (prepared statement নামেও পরিচিত)। SQL বাক্যে মানের জায়গায় একটি চিহ্ন (? বা :roll) রাখুন, আর মান আলাদাভাবে পাঠান। ডেটাবেস আগে বাক্যের কাঠামো বুঝে নেয়, তারপর মানটিকে শুধু ডেটা হিসেবে বসায় — ভেতরে যা-ই লেখা থাকুক, সেটা কখনো কমান্ড হয় না।

# নিরাপদ পথ: মান যায় আলাদাভাবে, SQL-এর লেখা কখনো জোড়া দেওয়া হয় না
roll = form_input                     # ব্যবহারকারী যা লিখেছেন
row = db.execute("SELECT name, gpa FROM students WHERE roll = ?", (roll,)).fetchone()

প্রায় সব ভাষায় একই ব্যবস্থা আছে — PHP-র PDO, Java-র PreparedStatement, Node.js-এর লাইব্রেরিগুলো। নিয়মটি মুখস্থ রাখুন: ব্যবহারকারীর দেওয়া কোনো মান কখনো জোড়া দিয়ে SQL বানাবেন না, সবসময় parameter হিসেবে পাঠাবেন। পাশাপাশি ওয়েবসাইটের ডেটাবেস-অ্যাকাউন্টকে শুধু দরকারি অনুমতি দিন (DCL-এর GRANT দিয়ে) — ফল দেখানোর পাতার DELETE বা DROP-এর অধিকার লাগে না। সাইবার নিরাপত্তার বড় ছবি পর্ব ৪৩-এ।

কোথায় অনুশীলন করবেন

  • DB Browser for SQLite: বিনামূল্যের ওপেন সোর্স ডেস্কটপ প্রোগ্রাম (Windows, macOS, Linux). একটি ফাইলে ডেটাবেস বানান, “Execute SQL” ট্যাবে প্রশ্ন লিখে চালান, টেবিল চোখে দেখুন। ইন্টারনেট ছাড়াই চলে।
  • অনলাইন SQL playground: ব্রাউজারেই SQL চালানোর অনেক ওয়েবসাইট আছে — “online SQL editor” বা “SQL fiddle” লিখে খুঁজুন। কিছু ইনস্টল করতে হয় না; শুধু গোপন বা আসল ডেটা সেখানে দেবেন না।
  • Microsoft Access: যাঁদের কম্পিউটারে Office-এর সাথে আছে, Create → Query Design থেকে SQL View-এ গিয়ে SQL লিখতে পারেন।
  • এই পর্বের দুটি চালানোর মতো ব্লক: ওপরের SQL লাইনগুলো বদলে বারবার চালান — ভুল করলে কিছুই নষ্ট হয় না, প্রতিবার নতুন ডেটাবেস তৈরি হয়।

আরও গভীরে: এখানে আপনি SQL-এর মূল অংশ দেখলেন। subquery, এক টেবিলের নিজের সাথে join (self join), window function, ER ডায়াগ্রাম থেকে টেবিল নকশা, normalization-এর গভীর রূপ আর transaction-এর isolation level শিখতে দেখুন ABCL TECH-এর DBMS ও SQL কোর্স. আর প্রোগ্রাম থেকে ডেটাবেস ব্যবহার — connection, commit, parameterized query আর migration — হাতে-কলমে শিখতে Python কোর্সের SQLite পর্ব.

পরীক্ষা ও ইন্টারভিউতে: HSC ICT, বিভিন্ন চাকরির কম্পিউটার অংশ আর জুনিয়র ডেভেলপারের ইন্টারভিউতে এ ধরনের প্রশ্ন প্রায়ই আসে —

  • SQL-এর পূর্ণরূপ কী? — Structured Query Language.
  • DDL আর DML-এর দুটি করে কমান্ড বলুন। — DDL: CREATE, ALTER, DROP; DML: INSERT, UPDATE, DELETE.
  • টেবিল পুরোপুরি মুছতে কোন কমান্ড, আর শুধু সারি মুছতে কোনটি? — DROP TABLE কাঠামোসহ সব মোছে; DELETE শুধু সারি মোছে, টেবিল থাকে। (TRUNCATE-ও সব সারি মোছে, দ্রুত — তবে সব ডেটাবেসে নেই।)
  • WHERE-এর কাজ কী? — শর্ত দিয়ে সারি ছাঁকা; শুধু শর্ত সত্য হওয়া সারিতে SELECT, UPDATE বা DELETE কাজ করে।
  • ORDER BY-এর ডিফল্ট ক্রম কী? — ASC, অর্থাৎ ছোট থেকে বড়। আর WHERE বনাম HAVING: WHERE সারি ছাঁকে, HAVING GROUP BY-এর পরে দল ছাঁকে।

৯ · AI-এর যুগে — SQL লেখে AI, চালানোর আগে যাচাই আপনার

ChatGPT, Gemini, Claude বা Copilot-কে বাংলায় বললেই — “প্রতিটি জেলার GPA ৫ পাওয়া ছাত্রের সংখ্যা দাও” — কয়েক সেকেন্ডে সঠিক দেখতে SQL লিখে দেয়। পড়তে নিখুঁত, চালালে ত্রুটিও দেখায় না। কিন্তু ভুল SQL প্রায়ই ত্রুটি দেয় না — ভুল ফল দেয়, অথবা ভুল সারি বদলে দেয়:

AI-এর ভুলফলআপনি যা করবেন
UPDATE বা DELETE-এ WHERE অসম্পূর্ণ বা নেইপুরো টেবিল বদলে বা মুছে যায়আগে একই WHERE দিয়ে SELECT; transaction-এর ভেতরে চালান
JOIN-এর শর্ত ভুল বা অসম্পূর্ণসারি দুই-তিনগুণ, যোগফল ফুলে ওঠেJOIN-এর আগে-পরে COUNT(*) মিলিয়ে দেখুন
= NULL লেখে, বা NULL ভুলে যায়কিছু সারি নীরবে বাদ পড়েনমুনা ডেটায় একটি NULL রেখে পরীক্ষা করুন
অন্য dialect-এর কমান্ড (MySQL-এ TOP)ত্রুটি, বা ভিন্ন আচরণপ্রশ্নে ডেটাবেসের নাম আর version বলুন
ব্যবহারকারীর ইনপুট জোড়া দিয়ে SQL বানায়SQL injection-এর দরজা খোলাparameterized query চান; জোড়া দেওয়া কোড ফেরত দিন

ভালো প্রশ্নে থাকে: ডেটাবেসের নাম, টেবিলের কাঠামো (আসল গোপন ডেটা নয়), কী চান, আর যাচাইয়ের অনুরোধ:

আমি SQLite ব্যবহার করি। টেবিল দুটি: students(roll INTEGER PRIMARY KEY, name TEXT, district TEXT, gpa REAL — gpa NULL হতে পারে) results(roll INTEGER, subject TEXT, marks INTEGER) ১. প্রতিটি জেলায় GPA 5 পাওয়া ছাত্রের সংখ্যা দেখানোর SQL দাও, জেলার নাম অনুযায়ী সাজিয়ে। ২. প্রতিটি অংশ (WHERE, GROUP BY, ORDER BY) এক লাইনে ব্যাখ্যা করো। ৩. ৬-৮ সারির একটি বানানো নমুনা ডেটা দাও, যাতে একটি NULL gpa আর এমন একটি জেলা থাকে যেখানে কেউ GPA 5 পায়নি — আর সেই ডেটায় প্রত্যাশিত ফল কী হবে, তা লিখে দাও। ৪. যদি কোনো UPDATE বা DELETE লেখো, WHERE আর transaction ছাড়া দেবে না; ব্যবহারকারীর মান থাকলে parameterized query ব্যবহার করবে।

AI-এর SQL চালানোর আগে — চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। পঞ্চাশ বছর পুরোনো একটি ভাষা কেন আজও ডেটার জগৎ শাসন করে, তার গল্প এখানে।

প্র ০১SQL কোথা থেকে এলো? IBM-এর SEQUEL থেকে ANSI মান পর্যন্ত গল্পটা কী?

১৯৭০ সালে IBM-এর গবেষক Edgar F. Codd একটি গবেষণাপত্রে রিলেশনাল মডেলের প্রস্তাব দেন — ডেটা থাকবে গাণিতিক “relation” বা টেবিলে, আর প্রশ্ন করা হবে যুক্তি দিয়ে, ডেটা কোথায় কীভাবে রাখা আছে তা না জেনেই। তখনকার ডেটাবেসে প্রোগ্রামারকে রেকর্ড থেকে রেকর্ডে পয়েন্টার ধরে “হেঁটে” যেতে হতো। Codd-এর ধারণা ছিল বিপ্লবী, কিন্তু তাঁর গাণিতিক ভাষা সাধারণ মানুষের পক্ষে লেখা কঠিন ছিল।

IBM-এর আরও দুই গবেষক, Donald Chamberlin আর Raymond Boyce, এমন একটি ভাষা বানাতে চাইলেন যা ইংরেজি বাক্যের মতো পড়া যায়। ১৯৭৪ সালে তাঁরা প্রকাশ করেন SEQUEL — Structured English Query Language. পরে যুক্তরাজ্যের একটি কোম্পানির ট্রেডমার্কের সাথে নামের মিল থাকায় নাম ছোট হয়ে দাঁড়ায় SQL — যদিও “সিকুয়েল” উচ্চারণ আজও টিকে আছে। IBM-এর পরীক্ষামূলক প্রকল্প System R-এ ভাষাটি পরীক্ষা করা হয়।

মজার ব্যাপার, প্রথম বাণিজ্যিক SQL ডেটাবেস IBM আনেনি। IBM-এর প্রকাশিত গবেষণা পড়ে Larry Ellison-এর ছোট একটি কোম্পানি ১৯৭৯ সালে বাজারে আনে Oracle — কোম্পানিটি পরে নিজেই এই নামে পরিচিত হয়। IBM-এর নিজের পণ্য (SQL/DS, পরে DB2) আসে কয়েক বছর পরে।

অনেক কোম্পানি নিজের মতো SQL বানাতে থাকায় একটি সাধারণ মানের দরকার হয়। ১৯৮৬ সালে আমেরিকার মান-সংস্থা ANSI SQL-কে আনুষ্ঠানিক মান হিসেবে গ্রহণ করে, পরের বছর আন্তর্জাতিক সংস্থা ISO-ও। এরপর SQL-92, SQL:1999 থেকে শুরু করে ২০২৩ সালের সংস্করণ পর্যন্ত মানটি বারবার হালনাগাদ হয়েছে — JSON, সময়-ভিত্তিক ডেটা, গ্রাফ-প্রশ্ন যুক্ত হয়েছে। কিন্তু ১৯৭০-এর দশকের SELECT … FROM … WHERE আজও হুবহু একই।

মূল উপলব্ধি: একটি ভাষা টিকে থাকে যখন তা একটি শক্ত গাণিতিক ভিত্তির ওপর দাঁড়িয়ে সাধারণ মানুষের পড়ার মতো সহজ হয়, আর একটি খোলা মান সবাইকে একই ভাষায় কথা বলতে দেয়।

প্র ০২“কী চাই” বলা আর “কীভাবে করব” বলার পার্থক্য কেন এত গুরুত্বপূর্ণ? declarative SQL কেন optimizer-কে পথ বাছতে দেয়?

C বা Python-এর মতো imperative ভাষায় আপনি ধাপগুলো লেখেন: এই লুপ, এই শর্ত, এই তালিকা। কম্পিউটার ঠিক সেভাবেই করে — আপনার পদ্ধতি ধীর হলে প্রোগ্রামও ধীর। SQL declarative: আপনি শুধু ফলাফলের বৈশিষ্ট্য বলেন। কীভাবে সেটা পাওয়া যাবে, সেই সিদ্ধান্ত ডেটাবেসের হাতে।

এই স্বাধীনতা কেন মূল্যবান? একটি সাধারণ JOIN-ও অনেক উপায়ে করা যায় — কোন টেবিল আগে পড়া হবে, index ব্যবহার হবে কি না, সারিগুলো মেলানো হবে লুপ করে, সাজিয়ে, না hash টেবিল বানিয়ে। পাঁচটি টেবিলের JOIN-এ সম্ভাব্য ক্রম শতাধিক। ১৯৭৯ সালে IBM-এর System R দলের Patricia Selinger ও সহকর্মীরা একটি পদ্ধতি প্রকাশ করেন, যেখানে ডেটাবেস প্রতিটি সম্ভাব্য পথের আনুমানিক খরচ (কতবার ডিস্ক পড়তে হবে) হিসাব করে সবচেয়ে সস্তাটি বেছে নেয়। এই cost-based optimizer-এর ধারণা আজও প্রায় সব বড় ডেটাবেসের ভিত্তি।

এর বড় সুবিধা: ডেটা বদলালে পরিকল্পনাও বদলায়, আপনার প্রশ্ন না বদলেই। আজ টেবিলে ১০০ সারি থাকলে পুরো টেবিল পড়াই দ্রুত; কাল ১ কোটি সারি হলে এবং কেউ একটি index যোগ করলে, একই SQL নিজে থেকেই index ব্যবহার শুরু করবে। ডেটাবেস নির্মাতারা নতুন কৌশল যোগ করলে পুরোনো প্রশ্নগুলোও দ্রুত হয়ে যায়। বেশিরভাগ ডেটাবেসে EXPLAIN লিখে দেখা যায় optimizer কোন পথ বেছেছে।

তবে স্বাধীনতার দামও আছে। optimizer-এর অনুমান ভুল হলে একটি প্রশ্ন হঠাৎ ধীর হয়ে যেতে পারে, আর কেন — তা বোঝা কঠিন। এজন্য বড় প্রতিষ্ঠানে “ডেটাবেস পারফরম্যান্স” আলাদা একটি দক্ষতা। HTML আর Excel-এর সূত্রও আংশিকভাবে declarative — আপনি বলেন কী দেখাতে হবে, ব্রাউজার বা Excel ঠিক করে কীভাবে।

মূল উপলব্ধি: “কী” আর “কীভাবে” আলাদা করলে প্রতিটি অংশ আলাদাভাবে উন্নত হতে পারে। ভালো বিমূর্ততা (পর্ব ২৩) ব্যবহারকারীকে সরল ভাষা দেয়, আর ভেতরের যন্ত্রকে বুদ্ধিমান হওয়ার সুযোগ।

প্র ০৩SQL injection এত পুরোনো আর সহজে ঠেকানো যায় — তবু কেন বছরের পর বছর বড় বড় তথ্য ফাঁসের কারণ হয়েছে? “Bobby Tables” কৌতুকটি কী শেখায়?

SQL injection প্রথম প্রকাশ্যে বিস্তারিতভাবে বর্ণিত হয় ১৯৯৮ সালের ডিসেম্বরে, Phrack নামের একটি নিরাপত্তা-পত্রিকায় Jeff Forristal-এর লেখায়। তখন ওয়েবসাইটগুলো সবে ডেটাবেসের সাথে যুক্ত হচ্ছে, আর বেশিরভাগ প্রোগ্রামার ব্যবহারকারীর ইনপুট সরাসরি SQL-এর লেখার সাথে জুড়ে দিতেন — কারণ সেটাই সবচেয়ে সহজ মনে হতো।

মূল সমস্যাটা হলো কোড আর ডেটা এক স্রোতে মিশে যাওয়া. ডেটাবেস একটি লেখা পায় এবং পুরোটাকেই কমান্ড হিসেবে পড়ে। ইনপুটের ভেতরে কোনো উদ্ধৃতিচিহ্ন বা SQL-এর মতো অংশ থাকলে সেটা কমান্ডের কাঠামো বদলে দিতে পারে। ২০০০-এর দশকে এর মাধ্যমে বেশ কয়েকটি বড় ঘটনা ঘটে — ২০০৮ সালের Heartland Payment Systems-এর ঘটনায় দশ কোটির বেশি পেমেন্ট কার্ডের তথ্য চুরি যায়, আর ২০১৫ সালে যুক্তরাজ্যের টেলিকম কোম্পানি TalkTalk-এর লক্ষাধিক গ্রাহকের তথ্য ফাঁস হয়। ওয়েব নিরাপত্তার সংগঠন OWASP-এর বহুল ব্যবহৃত “Top 10” ঝুঁকির তালিকায় injection ২০১০, ২০১৩ আর ২০১৭ সালে এক নম্বরে ছিল; ২০২১-এর তালিকায় তিন নম্বরে নেমেছে, পরে আরও কিছুটা নিচে — কমেছে, কিন্তু হারায়নি।

২০০৭ সালে Randall Munroe-র জনপ্রিয় ওয়েব কমিক xkcd-এর ৩২৭ নম্বর পর্বে (“Exploits of a Mom”) একটি স্কুল এক মায়ের কাছে ফোন করে জানায়, তাদের ছাত্রদের সব রেকর্ড হারিয়ে গেছে। কারণ, মা ছেলের নাম এমনভাবে রেখেছিলেন যে নামটির একটি অংশ SQL কমান্ড হিসেবে পড়লে Students টেবিল মুছে দেয়। ছেলের ডাকনাম তাই “Little Bobby Tables”. শেষে মায়ের পরামর্শ — “আশা করি এবার থেকে আপনারা ডেটাবেসের ইনপুট ঠিকমতো sanitize করবেন।” কৌতুকটি এত জনপ্রিয় যে নিরাপত্তা-প্রশিক্ষণে আজও দেখানো হয়।

তবু ভুলটা টিকে আছে কেন? পুরোনো কোড যা কেউ আর হাত দেয় না; নতুনদের টিউটোরিয়াল, যেখানে সহজতার জন্য জোড়া দেওয়া SQL দেখানো হয়; আর এখন AI-এর লেখা কোড, যা পুরোনো উদাহরণ থেকে শেখা। আধুনিক লাইব্রেরি parameterized query-কে সবচেয়ে সহজ পথ বানিয়েছে, তাই নতুন কোডে এটি কমছে।

মূল উপলব্ধি: নিরাপত্তার সবচেয়ে ভালো সমাধান হলো নিরাপদ পথটিকেই সবচেয়ে সহজ পথ বানানো। আর মৌলিক নিয়ম — কোড আর ডেটা সবসময় আলাদা চ্যানেলে।

প্র ০৪NULL কেন এত ঝামেলার? Tony Hoare-এর “billion-dollar mistake” আর SQL-এর তিন-মানের যুক্তি কী?

ব্রিটিশ কম্পিউটার বিজ্ঞানী Tony Hoare (Quicksort অ্যালগরিদমের উদ্ভাবক) ১৯৬৫ সালে ALGOL W নামের প্রোগ্রামিং ভাষায় “null reference” যোগ করেন — এমন একটি পয়েন্টার, যা কিছুই নির্দেশ করে না। ২০০৯ সালে লন্ডনের একটি সম্মেলনে (QCon) তিনি একে বলেন তাঁর “billion-dollar mistake”: গত কয়েক দশকে null-এর কারণে যত প্রোগ্রাম ক্র্যাশ, ত্রুটি আর নিরাপত্তা-সমস্যা হয়েছে, তার ক্ষতি সম্ভবত শত কোটি ডলার। প্রোগ্রামিং ভাষার null আর SQL-এর NULL হুবহু এক জিনিস নয়, কিন্তু দুটিই একই প্রশ্ন তোলে: “মান নেই” অবস্থাটাকে কীভাবে সামলাব?

SQL-এ NULL মানে “অজানা”. Codd-এর রিলেশনাল মডেল এর সাথে তিন-মানের যুক্তি (three-valued logic) এনেছে: সত্য, মিথ্যা, আর UNKNOWN. Mitu-র GPA অজানা — তাহলে “Mitu-র GPA = ৫?” এর উত্তর হ্যাঁ বা না নয়, “জানি না”. এমনকি NULL = NULL-ও UNKNOWN — দুটি অজানা মান সমান কি না, তা-ও অজানা। WHERE শুধু সত্য সারি রাখে, UNKNOWN বাদ দেয়। এজন্য WHERE gpa = NULL কিছুই দেয় না।

পর্ব ১৯-এর বুলিয়ান সত্য-সারণি এখানে বড় হয়: UNKNOWN AND FALSE = FALSE (একটি মিথ্যা থাকলেই পুরোটা মিথ্যা), কিন্তু UNKNOWN AND TRUE = UNKNOWN. UNKNOWN OR TRUE = TRUE. সবচেয়ে কুখ্যাত ফাঁদ: WHERE roll NOT IN (101, NULL) কোনো সারিই দেয় না, কারণ প্রতিটি সারির জন্য “roll ≠ NULL” অজানা থেকে যায়। আবার AVG, COUNT(gpa) NULL বাদ দিয়ে হিসাব করে — কখনো সেটাই চাওয়া, কখনো বিভ্রান্তিকর।

ডেটাবেস তাত্ত্বিক Chris Date বহু বছর ধরে যুক্তি দিয়েছেন যে NULL রিলেশনাল মডেলকেই দুর্বল করে; Codd নিজে পরে দুই ধরনের অনুপস্থিত মানের (“প্রযোজ্য কিন্তু অজানা” আর “প্রযোজ্যই নয়”) প্রস্তাব দেন। আধুনিক প্রোগ্রামিং ভাষাগুলো (Rust, Kotlin, Swift) null-কে টাইপ ব্যবস্থার ভেতরে স্পষ্ট করে নিয়ন্ত্রণ করে। ব্যবহারিক পরামর্শ: যে কলামে মান থাকা বাধ্যতামূলক, সেখানে NOT NULL দিন, আর NULL থাকতে পারে এমন কলামে প্রতিটি শর্ত NULL দিয়ে পরীক্ষা করুন।

মূল উপলব্ধি: “জানি না” একটি বৈধ উত্তর — কিন্তু তা যুক্তির নিয়ম বদলে দেয়। যে সিস্টেম অজানাকে স্পষ্টভাবে সামলায়, সেটাই নির্ভরযোগ্য।

প্র ০৫২০১০-এর দশকে “NoSQL” কেন জনপ্রিয় হলো, আর পরে SQL কীভাবে আবার ফিরে এলো?

২০০০-এর দশকে Google, Amazon, Facebook-এর মতো প্রতিষ্ঠানের ডেটা এমন আকারে পৌঁছায়, যা একটি বড় সার্ভারে আঁটে না — হাজার হাজার সাধারণ কম্পিউটারে ছড়িয়ে রাখতে হয়। প্রচলিত রিলেশনাল ডেটাবেস তখন মূলত একটি যন্ত্রে চলার জন্য তৈরি ছিল। Google ২০০৬ সালে Bigtable আর Amazon ২০০৭ সালে Dynamo নামের নিজস্ব ব্যবস্থার গবেষণাপত্র প্রকাশ করে, যেখানে কঠোর টেবিল-কাঠামো, JOIN আর পূর্ণ transaction-এর কিছু অংশ ছেড়ে দিয়ে বিপুল মাপে ছড়ানোর ক্ষমতা পাওয়া গিয়েছিল।

২০০৯ সালে সান ফ্রান্সিসকোর একটি মিটআপকে কেন্দ্র করে এই ধরনের ডেটাবেসের জন্য “NoSQL” নামটি জনপ্রিয় হয়। একই সময়ে আসে MongoDB (ডকুমেন্ট বা JSON-এর মতো রেকর্ড), Cassandra, Redis-এর মতো ব্যবস্থা। প্রতিশ্রুতি ছিল — schema নিয়ে মাথাব্যথা নেই, যত খুশি সার্ভার যোগ করুন। কিছু বছর মনে হচ্ছিল SQL পুরোনো হয়ে যাচ্ছে।

কিন্তু বাস্তবে বেশ কিছু সমস্যা দেখা দেয়। transaction না থাকলে টাকা-পয়সার হিসাবে অসঙ্গতি ঠেকানোর দায় পড়ে প্রোগ্রামারের ঘাড়ে। schema না থাকলেও ডেটার কাঠামো তো থাকেই — শুধু তা লুকিয়ে থাকে কোডের ভেতরে। আর বিশ্লেষক, ব্যবসায়ী, ডেটা সায়েন্টিস্ট — সবাই SQL জানেন, প্রতিটি NoSQL ব্যবস্থার আলাদা প্রশ্ন-ভাষা শিখতে চান না। ফলে উল্টো স্রোত শুরু হয়: Cassandra আনে SQL-এর মতো দেখতে CQL; বড় ডেটার Hadoop-এর ওপর আসে Hive-এর মতো SQL স্তর; Google ২০১২ সালে Spanner প্রকাশ করে — সারা পৃথিবীতে ছড়ানো, অথচ transaction আর SQL-সহ। “NewSQL” নামে নতুন ধারার ডেটাবেস দুই জগতের সুবিধা একসাথে দিতে চায়। আবার PostgreSQL-এর মতো রিলেশনাল ডেটাবেস JSON রাখার ক্ষমতা যোগ করে NoSQL-এর সুবিধার একটি বড় অংশ নিজেই দিয়ে দেয়।

আজকের চিত্র মিশ্র: ক্যাশের জন্য Redis, নির্দিষ্ট কাজে ডকুমেন্ট বা গ্রাফ ডেটাবেস — আর বেশিরভাগ ব্যবসার মূল ডেটা এখনো রিলেশনাল, SQL দিয়ে। ডেটা বিশ্লেষণের বড় বড় ক্লাউড প্ল্যাটফর্মও SQL-কেই প্রধান ভাষা রেখেছে।

মূল উপলব্ধি: প্রযুক্তির ঢেউ আসে-যায়, কিন্তু একটি ভালো বিমূর্ততা আর একটি বহুল প্রচলিত ভাষা খুব কঠিনে মরে। নতুন টুল বাছার আগে জিজ্ঞেস করুন — আমি কোন নিশ্চয়তা ছেড়ে দিচ্ছি, আর বিনিময়ে কী পাচ্ছি?

অনুশীলন

প্রথম তিনটি খাতা-কলমে করা যায়; আমাদের স্কুলের ডেটাবেস (students আর results) ধরে নিন। চাইলে ওপরের চালানোর মতো ব্লকে SQL বসিয়ে মিলিয়ে নিন।

  1. প্রশ্নকে SQL-এ রূপ দিন: (ক) ঢাকা বা খুলনার ছাত্রদের নাম আর GPA, GPA বড় থেকে ছোট। (খ) যাদের নামে “a” অক্ষর দ্বিতীয় স্থানে। (গ) প্রতিটি ছাত্রের তিন বিষয়ের মোট নম্বর, মোট ২৪০-এর বেশি হলে শুধু তারা। প্রতিটির প্রত্যাশিত ফলও লিখুন।

    (ক) SELECT name, gpa FROM students WHERE district IN ('Dhaka', 'Khulna') ORDER BY gpa DESC; — Fatema 5.00, Sadia 5.00, Karim 4.50, আর Mitu (NULL). SQLite-এ DESC সাজালে NULL শেষে আসে। Fatema আর Sadia-র ক্রম নিশ্চিত করতে , name যোগ করুন। (খ) WHERE name LIKE '_a%' — Rahim, Karim, Fatema, Tanvir, Sadia. (গ) SELECT roll, SUM(marks) AS total FROM results GROUP BY roll HAVING SUM(marks) > 240 ORDER BY total DESC; — মোট দাঁড়ায় 104 (257), 101 (255), 103 (251), 106 (247); 102 (223) আর 105 (173) বাদ। এখানে WHERE চলবে না, কারণ শর্তটি যোগফলের ওপর — দলের ওপর।

  2. NULL-এর ধাঁধা: students টেবিলে নিচের প্রতিটি প্রশ্ন কয়টি সারি দেবে, আগে অনুমান করুন: (ক) WHERE gpa = NULL (খ) WHERE gpa IS NULL (গ) WHERE gpa < 5 (ঘ) WHERE NOT gpa < 5. (গ) আর (ঘ) মিলে কি আট হয়?

    (ক) ০ — NULL-এর সাথে = সবসময় UNKNOWN. (খ) ১ — Mitu. (গ) ৪ — Karim, Nusrat, Tanvir, Arif. (ঘ) ৩ — Rahim, Fatema, Sadia. মোট ৭, আট নয়! Mitu দুটিতেই বাদ, কারণ NULL < 5 UNKNOWN, আর NOT UNKNOWN-ও UNKNOWN. সাধারণ যুক্তিতে “শর্ত বা তার বিপরীত — একটা তো সত্য হবেই”, কিন্তু তিন-মানের যুক্তিতে নয়। রিপোর্টে সারির সংখ্যা না মিললে প্রথমেই NULL খুঁজুন।

  3. কোন কমান্ড? প্রতিটি পরিস্থিতিতে কোন SQL কমান্ড, আর সেটা DDL না DML — (ক) নতুন শিক্ষাবর্ষে results-এর সব পুরোনো নম্বর মুছতে হবে, টেবিল থাকবে। (খ) subjects টেবিলটি আর লাগবে না। (গ) students-এ ইমেইলের কলাম যোগ। (ঘ) একজনের জেলা ভুল লেখা হয়েছে।

    (ক) DELETE FROM results; — DML; এখানে WHERE ছাড়া ইচ্ছাকৃত, তবু আগে backup আর transaction. কিছু ডেটাবেসে TRUNCATE TABLE results; দ্রুততর। (খ) DROP TABLE subjects; — DDL; তবে results-এর foreign key যদি subjects-কে নির্দেশ করে, ডেটাবেস আপত্তি করতে পারে — আগে সেই সম্পর্ক মীমাংসা করতে হবে। (গ) ALTER TABLE students ADD COLUMN email VARCHAR(100); — DDL. (ঘ) UPDATE students SET district = 'Bogura' WHERE roll = 107; — DML; WHERE-এ primary key, তাই ঠিক একটি সারি।

  4. হাতে-কলমে, DB Browser for SQLite-এ: একটি নতুন ডেটাবেস ফাইল বানিয়ে students টেবিল তৈরি করুন (CHECK-সহ), পাঁচটি সারি ঢোকান। তারপর (ক) gpa 6 দিয়ে একটি সারি ঢোকানোর চেষ্টা করুন, (খ) একই roll দুবার ঢোকান, (গ) WHERE ছাড়া একটি UPDATE চালান, Browse Data ট্যাবে ফল দেখুন, তারপর ওপরের “Revert Changes” বোতাম চাপুন। প্রতিবার কী বার্তা এলো লিখে রাখুন।

    (ক) “CHECK constraint failed” ধরনের ত্রুটি — সারি ঢোকে না। (খ) “UNIQUE constraint failed: students.roll” — primary key-র দুবার হওয়া চলবে না। (গ) UPDATE পাঁচটি সারিই বদলায় (“5 rows affected”), টেবিল দেখলে সবার মান একই; “Revert Changes”-এর পরে আবার দেখুন — সব আগের মতো। কারণ DB Browser বদলগুলো একটি খোলা transaction-এ রাখে, “Write Changes” (COMMIT) না চাপা পর্যন্ত স্থায়ী করে না; Revert মানে ROLLBACK. ত্রুটির বার্তাগুলো শত্রু নয়: এগুলোই প্রমাণ যে constraint আপনার ডেটা পাহারা দিচ্ছে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি SQL SELECT প্রশ্ন ডেটাবেস যে যৌক্তিক ক্রমে প্রক্রিয়া করে, অংশগুলো সেই ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📂 FROM — কোন টেবিল (আর JOIN)
  2. 🔍 WHERE — সারি ছাঁকা
  3. 🧺 GROUP BY — দলে ভাগ
  4. 🧮 HAVING — দল ছাঁকা
  5. 📋 SELECT — কলাম বাছাই আর হিসাব
  6. ↕️ ORDER BY — সাজানো

নিচের কোনগুলো সবই DDL (Data Definition Language)-এর কমান্ড?

  • INSERT, UPDATE, DELETE
  • CREATE, ALTER, DROP
  • SELECT, GRANT, REVOKE
  • COMMIT, ROLLBACK, UPDATE

SQL-এ ORDER BY marks লিখলে, ASC বা DESC কিছু না দিলে, ফলাফল কোন ক্রমে সাজানো হয়?

  • যে ক্রমে ডেটা ঢোকানো হয়েছিল
  • বড় থেকে ছোট
  • ছোট থেকে বড়
  • ত্রুটি দেখাবে, কারণ ক্রম বলা বাধ্যতামূলক

একটি টেবিলের সব সারি মুছে ফেলতে হবে, কিন্তু টেবিলের কাঠামো (কলামগুলো) রেখে দিতে হবে। কোন কমান্ড উপযুক্ত?

  • DROP TABLE results;
  • ALTER TABLE results;
  • SELECT * FROM results;
  • DELETE FROM results;

“নামের শেষ অক্ষর a” — এমন সব ছাত্র খুঁজতে কোন শর্তটি সঠিক?

  • WHERE name LIKE '%a'
  • WHERE name LIKE 'a%'
  • WHERE name LIKE '_a'
  • WHERE name = '%a'

results টেবিলে প্রতিটি বিষয়ের গড় নম্বর বের করে শুধু যেসব বিষয়ের গড় ৭৫-এর বেশি, সেগুলো দেখাতে হবে। কোন প্রশ্নটি সঠিক?

  • SELECT subject, AVG(marks) FROM results WHERE AVG(marks) > 75 GROUP BY subject;
  • SELECT subject, AVG(marks) FROM results GROUP BY subject HAVING AVG(marks) > 75;
  • SELECT subject, AVG(marks) FROM results WHERE marks > 75 GROUP BY subject;
  • SELECT subject, AVG(marks) FROM results HAVING marks > 75;

একটি ওয়েবসাইটে ব্যবহারকারী রোল নম্বর লিখে ফল দেখেন। SQL injection প্রতিরোধের সবচেয়ে নির্ভরযোগ্য উপায় কোনটি?

  • টেবিলের নাম গোপন রাখা
  • শুধু ব্রাউজারে JavaScript দিয়ে ইনপুট যাচাই
  • Parameterized query — মান ? চিহ্নের জায়গায় আলাদাভাবে পাঠানো
  • ইনপুটের চারপাশে উদ্ধৃতিচিহ্ন বসিয়ে SQL-এর সাথে জোড়া দেওয়া

students (৮ জন) আর results (৬ জনের ফল) টেবিল roll দিয়ে জোড়া হলো। কোন JOIN ফলাফলে সব ৮ জন ছাত্রকেই রাখবে, যাদের ফল নেই তাদের marks-এ NULL বসিয়ে?

  • students LEFT JOIN results
  • students INNER JOIN results
  • students JOIN results
  • শর্ত ছাড়া দুই টেবিল জোড়া

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. SQL declarative — আপনি বলেন কী চান, ডেটাবেসের optimizer ঠিক করে কীভাবে। DDL (CREATE, ALTER, DROP), DML (INSERT, UPDATE, DELETE), DQL (SELECT), DCL (GRANT).
  2. টেবিল বানানোর সময় data type আর constraint (PRIMARY KEY, NOT NULL, UNIQUE, CHECK, DEFAULT, FOREIGN KEY) — ভুল ডেটা ঢোকার আগেই আটকায়।
  3. SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT; কাজের ক্রম শুরু হয় FROM থেকে। ORDER BY-এর ডিফল্ট ASC; NULL খুঁজতে IS NULL.
  4. WHERE ছাঁকে সারি, HAVING ছাঁকে দল; INNER JOIN শুধু মিল পাওয়া সারি, LEFT JOIN বাঁয়ের সবাই।
  5. UPDATE/DELETE-এর আগে একই WHERE দিয়ে SELECT, ঝুঁকির কাজ transaction-এ; ব্যবহারকারীর মান সবসময় parameterized query-তে — আর AI-এর SQL নমুনা ডেটায় যাচাই করে তবেই।

পরের পর্বে: SQL injection ছিল নিরাপত্তার একটি দরজা মাত্র। এবার নতুন মডিউল — সাইবার নিরাপত্তা: শক্তিশালী পাসওয়ার্ড, 2FA, phishing চেনা আর ম্যালওয়্যার থেকে নিজেকে, পরিবারকে আর অফিসকে বাঁচানোর উপায়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান