SQL Setup & DDL — CREATE, ALTER, DROP
SQL সেটআপ ও DDL — CREATE, ALTER, DROP
1. Why DDL Comes First
Before you can store a single row of data, you must first describe the shape of that data to the database. This is the job of DDL — Data Definition Language — the subset of SQL that creates, alters and drops the structures (tables, columns, indexes) where rows eventually live. DDL is the architect's blueprint; DML (which we cover in the next module) is the bricklayer's hand.
By the end of this module you will be able to: open the in-browser SQLite, design a table for a real-world
domain (students, orders, bKash transactions), pick the right data types, enforce business rules through
PRIMARY KEY, NOT NULL, UNIQUE, DEFAULT, CHECK
and FOREIGN KEY, and modify a schema safely without losing data.
একটি table আসলে একটি type-যুক্ত spreadsheet — প্রতিটি column-এর একটি নাম, একটি type এবং কিছু নিয়ম থাকে। এটা মাথায় গেঁথে গেলে SQL-এর বাকি সব কিছু সহজ হয়ে যাবে।
2. The In-Browser SQLite — Zero Install
Throughout this entire course, every code block on this page runs SQLite compiled to
WebAssembly — directly in your browser. There is no server, no Docker container, no apt-get install.
Click Run ▶ and a fresh in-memory database is created, your SQL is executed, the result is
rendered as a table, and the database is thrown away. This is identical to how the production
sqlite3 command-line tool behaves with :memory:.
sqlite3 :memory: command যেভাবে কাজ করে, এটি ঠিক সেভাবেই কাজ করে।
Try it now. The block below creates a tiny one-row table and queries it:
SELECT 'Hello, Bangladesh!' AS greeting,
2 + 2 AS arithmetic;
3. Data Types — SQLite vs PostgreSQL vs MySQL
Every column has a type — a promise about the kind of value that column will hold. The three databases you will meet most often in industry treat types slightly differently, and being aware of those differences early will save you painful migration bugs later.
| You want to store… | SQLite | PostgreSQL | MySQL | Notes |
|---|---|---|---|---|
| Whole numbers | INTEGER | INT / BIGINT | INT / BIGINT | SQLite stores as 64-bit signed. |
| Decimal money | NUMERIC or REAL | NUMERIC(10,2) | DECIMAL(10,2) | Never use FLOAT/REAL for money. |
| Short text | TEXT | VARCHAR(n) / TEXT | VARCHAR(n) | SQLite ignores length limits. |
| Long text / JSON | TEXT | TEXT / JSONB | TEXT / JSON | Postgres JSONB is the industry standard. |
| True / False | INTEGER (0/1) | BOOLEAN | TINYINT(1) | SQLite has no native boolean. |
| Date | TEXT ISO-8601 | DATE | DATE | SQLite recommends 'YYYY-MM-DD'. |
| Date + time | TEXT ISO-8601 | TIMESTAMP | DATETIME | Always store in UTC. |
| Binary blob | BLOB | BYTEA | BLOB | For files, prefer S3 + a TEXT URL. |
NULL, INTEGER, REAL, TEXT, BLOB) and the type you write is treated as a hint, not a strict rule. Writing VARCHAR(20) is legal — SQLite simply maps it to TEXT and ignores the 20.
SQLite-এ মাত্র পাঁচটি storage class থাকে এবং আপনি যে type লেখেন সেটা একটি হিন্ট মাত্র, কঠোর নিয়ম নয়। তাই
VARCHAR(20) লিখলেও সেটা TEXT হিসেবেই save হয় এবং দৈর্ঘ্যের সীমা প্রয়োগ হয় না।
4. CREATE TABLE — The Heart of DDL
The CREATE TABLE statement names a new table and lists its columns. Each column has a name,
a type, and zero or more constraints. Constraints are non-negotiable rules that the
database itself enforces — so even if your application code has a bug, the database refuses to store
invalid data. This is one of the most important reasons to push business rules into the schema.
CREATE TABLE statement দিয়ে একটি নতুন table-এর নাম এবং তার column-গুলোর তালিকা দেওয়া হয়। প্রতিটি column-এর একটি নাম, একটি type এবং কিছু constraint থাকতে পারে। Constraint মানে এমন কিছু নিয়ম যা database নিজে enforce করে — application কোডে bug থাকলেও invalid ডেটা ঢুকতে পারবে না। এজন্য business rule যতটা সম্ভব database schema-তে রাখাই ভালো।
4.1 — A real schema: students at a Bangladeshi university
-- Create a table for students at a university
CREATE TABLE students (
id INTEGER PRIMARY KEY,
full_name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
cgpa REAL DEFAULT 0.0,
department TEXT NOT NULL DEFAULT 'CSE',
is_active INTEGER NOT NULL DEFAULT 1,
enrolled_on TEXT NOT NULL DEFAULT (date('now')),
CHECK (cgpa >= 0.0 AND cgpa <= 4.0)
);
-- Insert two students so we can see something in the result
INSERT INTO students (full_name, email, cgpa, department)
VALUES
('Arif Hossain', 'arif@nsu.edu.bd', 3.85, 'CSE'),
('Sanjida Akter', 'sanjida@du.ac.bd', 3.92, 'EEE');
SELECT id, full_name, email, cgpa, department
FROM students;
| Piece | Meaning | বাংলায় |
|---|---|---|
INTEGER PRIMARY KEY | Unique row id; SQLite auto-increments it. | Row-এর unique পরিচয়; SQLite নিজে থেকে বাড়ায়। |
NOT NULL | Refuses to insert a row that leaves the column empty. | Column খালি রেখে row insert করা যাবে না। |
UNIQUE | No two rows may share the same value. | একই value দুবার থাকতে পারবে না। |
DEFAULT 'CSE' | If you don't specify a value, this is what the database stores. | আপনি value না দিলে এটি default হিসেবে save হবে। |
DEFAULT (date('now')) | An expression-default — runs at insert time. | Insert-এর সময় expression চালিয়ে value বসায়। |
CHECK (cgpa BETWEEN 0 AND 4) | Refuses any insert where the rule is violated. | নিয়ম ভাঙলে database row নিতে অস্বীকার করবে। |
4.2 — Constraints in action: a CHECK that rejects bad data
-- This insert has cgpa = 5.7 which violates the CHECK rule.
-- SQLite will refuse it and stop right here.
INSERT INTO students (full_name, cgpa)
VALUES ('Hacker Habib', 5.7);
You should see an error message like CHECK constraint failed — exactly what we want.
5. Foreign Keys — Linking Tables Together
A foreign key says: "the value in this column must match an existing primary-key value
in another table." This is what turns a pile of tables into a relational database. Without foreign
keys you can have an orders row referring to a customer_id that does not exist —
an orphan record — and your reports start lying.
-- Enable FK enforcement (SQLite turns it off by default for legacy reasons)
PRAGMA foreign_keys = ON;
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
phone TEXT UNIQUE
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
amount NUMERIC NOT NULL CHECK(amount > 0),
placed_on TEXT DEFAULT (datetime('now')),
FOREIGN KEY (customer_id) REFERENCES customers(id)
ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO customers(name, phone) VALUES
('Rahim Uddin', '01711-100100'),
('Karim Mia', '01911-200200');
INSERT INTO orders(customer_id, amount) VALUES
(1, 1499.50),
(2, 320.00);
SELECT o.id, c.name, o.amount
FROM orders o
JOIN customers c ON c.id = o.customer_id;
| Action clause | What happens | বাংলায় |
|---|---|---|
ON DELETE RESTRICT | Refuses to delete a parent that still has children. | Child থাকলে parent মুছে ফেলা যাবে না। |
ON DELETE CASCADE | Deletes children automatically with the parent. | Parent মুছলে child-ও স্বয়ংক্রিয়ভাবে মুছে যাবে। |
ON DELETE SET NULL | Sets the FK column to NULL when the parent dies. | Parent মুছলে FK column NULL হয়ে যাবে। |
ON UPDATE CASCADE | Propagates a parent-id change to all children. | Parent-এর id পরিবর্তন হলে সেটি child-এও বসবে। |
6. ALTER TABLE — Evolving the Schema
Real applications evolve. New requirements arrive, columns get renamed, new fields are added. ALTER TABLE
lets you change a table's structure without dropping its data. SQLite supports four flavours:
add column, rename column, rename table, and drop column (since 3.35).
ALTER TABLE দিয়ে ডেটা না হারিয়ে এই পরিবর্তনগুলো করা যায়। SQLite-এ চারটি প্রধান অপারেশন আছে — column যোগ, column-এর নাম পরিবর্তন, table-এর নাম পরিবর্তন এবং column drop (৩.৩৫ সংস্করণ থেকে)।
-- 1) Add a new column with a sensible default
ALTER TABLE products ADD COLUMN stock INTEGER NOT NULL DEFAULT 0;
-- 2) Rename a column (better naming = clearer code)
ALTER TABLE products RENAME COLUMN name TO title;
-- 3) Rename the table itself
ALTER TABLE products RENAME TO catalog_items;
-- 4) Verify the new shape
SELECT * FROM catalog_items;
CREATE TABLE products_new (...)with the new shape.INSERT INTO products_new SELECT … FROM products.DROP TABLE products.ALTER TABLE products_new RENAME TO products.
একই statement দিয়ে SQLite-এ column-এর type পরিবর্তন বা constraint drop করা যায় না। সমাধান হলো — নতুন shape-এ একটি table বানিয়ে, পুরনো ডেটা copy করে, পুরনো table drop করে, নতুনটিকে rename করা।
7. DROP TABLE vs TRUNCATE vs DELETE
Three different statements can "remove" things, and confusing them is one of the most expensive mistakes a junior engineer can make. The table below makes the differences crystal clear.
| Statement | Removes… | Keeps schema? | Triggers fire? | Reversible inside a transaction? |
|---|---|---|---|---|
DELETE FROM t; | All rows (one by one) | Yes | Yes | Yes (ROLLBACK works) |
DELETE FROM t WHERE …; | Matching rows only | Yes | Yes | Yes |
TRUNCATE TABLE t; (Postgres / MySQL) | All rows (fast, set-based) | Yes | Often skipped | Postgres: yes · MySQL: usually no |
DROP TABLE t; | The whole table — schema and data | No — table ceases to exist | n/a | Yes (Postgres) · Yes (SQLite, inside a tx) |
DELETE row মুছে কিন্তু table-এর গঠন রাখে এবং transaction-এর ভেতরে rollback করা যায়। TRUNCATE দ্রুত সব row মুছে দেয় কিন্তু MySQL-এ rollback সম্ভব নয়। DROP TABLE পুরো table-ই মুছে দেয় — schema এবং ডেটা দুটোই। এই তিনটিকে এক করে ফেললে production-এ বিপদ — তাই খুব ভালোভাবে চিনে রাখা জরুরি।
SQLite does not have TRUNCATE, but it optimises DELETE FROM t (without a WHERE) into a fast O(1) operation internally — called the "truncate optimization":
SELECT COUNT(*) AS before_delete FROM bkash_tx;
-- Wipe all rows (schema preserved)
DELETE FROM bkash_tx;
SELECT COUNT(*) AS after_delete FROM bkash_tx;
-- Schema still exists — we can insert again
INSERT INTO bkash_tx(sender, amount) VALUES ('01911-999999', 9999);
SELECT * FROM bkash_tx;
DROP TABLE or DELETE FROM directly into a production console. Wrap
destructive statements in a transaction, run a SELECT first to confirm the row count, and
only then commit. We will revisit this in Module 12.
Production-এ সরাসরি
DROP TABLE বা WHERE-হীন DELETE চালানো অত্যন্ত বিপজ্জনক। আগে SELECT COUNT(*) দিয়ে যাচাই করুন, transaction-এ মোড়ান, তারপর commit করুন।
8. Quick Reference — Constraints That Save Lives
✅ Always set (সবসময় দিন)
PRIMARY KEY— every table needs one identity column.NOT NULLon columns the business cannot live without.UNIQUEon natural keys (email, phone, NID).FOREIGN KEYon every "id-of-another-table" column.DEFAULTfor created_at, status, currency.
⚠️ Common mistakes (সাধারণ ভুল)
- Storing money in
FLOAT/REAL. - Storing dates as
"12/03/2024"— pick ISO-8601. - Forgetting
PRAGMA foreign_keys = ONin SQLite. - Using
VARCHAR(20)in SQLite expecting it to truncate. - Dropping a table when you only meant to delete its rows.
9. Practice Problems
Each problem has a Show Answer button. Most answers are runnable — click Run to see the result on the spot. Solve it yourself first, then check.
-
Create a table
teacherswith columnsid(PK),name(NOT NULL),email(UNIQUE),salary(positive only). Insert two rows and select them.একটিteacherstable বানান যাতেid,name,emailএবংsalaryথাকবে; দুটি row insert করে দেখান।✨ Show Answer (উত্তর দেখুন)
ans1.sqlCREATE TABLE teachers ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE, salary NUMERIC CHECK (salary > 0) ); INSERT INTO teachers(name, email, salary) VALUES ('Prof. Jamilur Rahman', 'jamil@buet.ac.bd', 85000), ('Dr. Tania Ahmed', 'tania@du.ac.bd', 92000); SELECT * FROM teachers; -
Add a
phonecolumn (TEXT, UNIQUE) to an existingcustomerstable.বিদ্যমানcustomerstable-এ একটিphonecolumn যোগ করুন।✨ Show Answer
ans2.sqlALTER TABLE customers ADD COLUMN phone TEXT UNIQUE; UPDATE customers SET phone = '01711-000001' WHERE id = 1; UPDATE customers SET phone = '01711-000002' WHERE id = 2; SELECT * FROM customers; -
Rename the column
nameincustomerstofull_name.customerstable-এnamecolumn-এর নাম বদলেfull_nameকরুন।✨ Show Answer
ans3.sqlALTER TABLE customers RENAME COLUMN name TO full_name; SELECT * FROM customers; -
Create a table
library_bookswhereisbnis the primary key andcopiesdefaults to 1.একটিlibrary_bookstable বানান, যেখানেisbnprimary key এবংcopiesএর default value ১।✨ Show Answer
ans4.sqlCREATE TABLE library_books ( isbn TEXT PRIMARY KEY, title TEXT NOT NULL, author TEXT NOT NULL, copies INTEGER NOT NULL DEFAULT 1 CHECK(copies >= 0) ); INSERT INTO library_books(isbn, title, author) VALUES ('978-984-401-001-1', 'Pother Pachali', 'Bibhutibhushan'); SELECT * FROM library_books; -
Drop the
teacherstable if it exists, then verify it is gone usingsqlite_master.teacherstable-টি drop করুন এবং পরেsqlite_masterদিয়ে যাচাই করুন।✨ Show Answer
ans5.sqlDROP TABLE IF EXISTS teachers; SELECT name FROM sqlite_master WHERE type = 'table'; -
Design two related tables
departmentsandemployeeswith a foreign key from employee to department.দুটি সম্পর্কিত table —departmentsএবংemployees— foreign key সহ ডিজাইন করুন।✨ Show Answer
ans6.sqlPRAGMA foreign_keys = ON; CREATE TABLE departments ( id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE ); CREATE TABLE employees ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, department_id INTEGER NOT NULL, FOREIGN KEY (department_id) REFERENCES departments(id) ); INSERT INTO departments(name) VALUES ('Engineering'), ('Finance'); INSERT INTO employees(name, department_id) VALUES ('Mahmud', 1), ('Sabina', 2); SELECT e.name, d.name AS department FROM employees e JOIN departments d ON d.id = e.department_id; -
Why does SQLite store dates as TEXT rather than DATE? Answer in two sentences.SQLite কেন date-কে TEXT হিসেবে রাখে, DATE নয়? দুই বাক্যে ব্যাখ্যা করুন।
✨ Show Answer
Answer: SQLite was designed as an extremely small embedded engine with only five storage classes (
NULL,INTEGER,REAL,TEXT,BLOB) and no native date type. Storing dates as ISO-8601TEXT('YYYY-MM-DD HH:MM:SS') keeps the engine tiny while still allowing correct lexicographic ordering and the fulldate()/strftime()function family.SQLite-কে অত্যন্ত ছোট embedded engine হিসেবে ডিজাইন করা হয়েছে; এতে মাত্র পাঁচটি storage class আছে এবং কোনো native date type নেই। ISO-8601 ফরম্যাটে TEXT হিসেবে date রাখলেই engine ছোট থাকে, lexicographic sort ঠিকঠাক কাজ করে এবং
date()/strftime()ফাংশনগুলোও ব্যবহার করা যায়। -
Add a CHECK constraint to ensure mobile-recharge amount is between 10 and 5000 BDT.Mobile recharge amount ১০ থেকে ৫০০০ টাকার মধ্যে আছে কিনা যাচাইয়ের জন্য CHECK যোগ করুন।
✨ Show Answer
ans8.sqlCREATE TABLE recharges ( id INTEGER PRIMARY KEY, phone TEXT NOT NULL, amount NUMERIC NOT NULL CHECK(amount BETWEEN 10 AND 5000) ); INSERT INTO recharges(phone, amount) VALUES ('01911-111111', 50), ('01911-222222', 1000); SELECT * FROM recharges; -
Use
CREATE TABLE IF NOT EXISTSto make a "first run safe" creation script.CREATE TABLE IF NOT EXISTSদিয়ে এমন script লিখুন যা প্রথম এবং পরবর্তী রান উভয়েই কাজ করবে।✨ Show Answer
ans9.sqlCREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY, body TEXT NOT NULL, created TEXT DEFAULT (datetime('now')) ); -- Running this twice is harmless; the second time is a no-op. CREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY, body TEXT ); SELECT name FROM sqlite_master WHERE type = 'table'; -
Create a composite primary key (course_id, student_id) for an
enrollmentstable.একটিenrollmentstable-এ (course_id, student_id) — composite primary key দিন।✨ Show Answer
ans10.sqlCREATE TABLE enrollments ( course_id INTEGER NOT NULL, student_id INTEGER NOT NULL, grade TEXT, PRIMARY KEY (course_id, student_id) ); INSERT INTO enrollments VALUES (101, 1, 'A'), (101, 2, 'B+'); SELECT * FROM enrollments; -
Make a
productstable wherepricemust be greater than zero andstocknon-negative.এমনproductstable বানান যেখানেprice০-এর বেশি ওstocknon-negative।✨ Show Answer
ans11.sqlCREATE TABLE products ( id INTEGER PRIMARY KEY, title TEXT NOT NULL, price NUMERIC NOT NULL CHECK(price > 0), stock INTEGER NOT NULL DEFAULT 0 CHECK(stock >= 0) ); INSERT INTO products(title, price, stock) VALUES ('Walton fan', 3500, 12), ('Pran juice 1L', 95, 200); SELECT * FROM products; -
List the difference between
DELETEandDROPin three short bullet points.তিনটি ছোট bullet-এDELETEএবংDROP-এর পার্থক্য লিখুন।✨ Show Answer
DELETEremoves rows; the table and its schema remain.DROPremoves the entire table — schema, indexes, triggers, everything.DELETEcan be filtered byWHERE;DROPcannot.
DELETEশুধু row মুছে;DROPপুরো table মুছে দেয়। -
In one statement, drop a column from an existing SQLite table (3.35+).SQLite ৩.৩৫+ ব্যবহার করে এক statement-এ একটি column drop করুন।
✨ Show Answer
ans13.sqlALTER TABLE temp_t DROP COLUMN b; SELECT * FROM temp_t; -
Why is putting
NOT NULLon a column "free protection"? Explain in 2 sentences.কেন একটি column-এNOT NULLদেওয়া "ফ্রি সুরক্ষা"? দুই বাক্যে ব্যাখ্যা করুন।✨ Show Answer
Answer: A NULL value silently disables many query semantics — comparisons return UNKNOWN, aggregates skip the row, and joins behave unexpectedly. Forbidding NULLs at the database level eliminates an entire class of bugs that the application would otherwise have to handle case by case.
NULL value-এর কারণে অনেক query চুপচাপ ভুল ফল দেয় — তুলনা UNKNOWN হয়, aggregate row বাদ দেয়, join-এর আচরণ বদলায়। তাই database-এই NULL forbid করলে অনেক bug-ই আগে থেকে আটকে যায়।
Summary — Module 11
DDL defines the shape of a database: CREATE TABLE brings tables into
existence, ALTER TABLE evolves them, DROP TABLE removes them. Every column has a
type and zero or more constraints (PRIMARY KEY, NOT NULL,
UNIQUE, DEFAULT, CHECK, FOREIGN KEY) — push your business
rules here so the database itself enforces them. SQLite has only five storage classes and treats type
declarations as hints, while PostgreSQL and MySQL are strict — keep the table from §3 nearby until the
differences feel natural.
CREATE দিয়ে table তৈরি, ALTER দিয়ে পরিবর্তন, DROP দিয়ে মুছে ফেলা। প্রতিটি column-এর type এবং কিছু constraint (PRIMARY KEY, NOT NULL, UNIQUE, DEFAULT, CHECK, FOREIGN KEY) থাকে — business rule যতটা সম্ভব এখানেই রাখুন। SQLite-এর type system নরম, কিন্তু PostgreSQL এবং MySQL কঠোর — পার্থক্যগুলো মনে রাখা জরুরি।