অধ্যায় 1 · ডেটাবেস ও SQL
সেটআপ: আপনার অনুশীলনের ডেটাবেস
- পৃষ্ঠা 3 / 22
- 17 মিনিট পড়া
এই টিউটোরিয়ালের প্রতিটা উদাহরণ চলে একটা ছোট ফাইলের ওপর, shop.db: BitByte Shop ডেটাবেস। এই পাতায় একটা স্ক্রিপ্ট থেকে সেটা বানাবেন, ভেতরে দেখার তিনটা উপায় শিখবেন, আর পাবেন একটা ছোট্ট Python হেল্পার, যা কোয়েরির ফলাফল সুন্দর টেবিলে দেখায় আর কিছু ভেঙে ফেললেই ডেটাকে শুরুর অবস্থায় ফিরিয়ে দেয়। ভাঙতে ভয় পাবেন না — রিসেট করতে এক সেকেন্ডও লাগে না।
নতুন কিছু লাগবে না: SQLite Python-এর সাথেই আসে। চাইলে একটা আসল MySQL বা PostgreSQL সার্ভারও চালাতে পারেন (ঐচ্ছিক) — পাতার দ্বিতীয় অর্ধেকে দেখানো আছে কীভাবে একটা মাত্র Docker কমান্ডে সেটা চালু করবেন।
যা শিখবেন
- SQLite কাজের জন্য তৈরি কি না আর কোন সংস্করণ আছে, তা কীভাবে দেখবেন
- Python-এ ধাপে ধাপে
shop.sqlথেকেshop.dbকীভাবে বানাবেন sqlhelp.pyকীভাবে ব্যবহার করবেন: ফলাফল দেখতেshow(), নতুন করে শুরু করতেreset()- Python-এর বদলে DB Browser for SQLite বা
sqlite3শেল দিয়ে কীভাবে কাজ করবেন - একই ডেটাবেস MySQL বা PostgreSQL-এ কীভাবে চালাবেন, আর কোন ক্লায়েন্ট ব্যবহার করবেন
SQLite আগে থেকেই ইনস্টল করা আছে
SQLite এমন কোনো সার্ভার নয় যা ইনস্টল করে চালু করতে হয়: এটা একটা লাইব্রেরি, আর Python এটা sqlite3 মডিউল হিসেবে সাথে নিয়েই আসে। দেখে নিন:
import sqlite3
print("SQLite version:", sqlite3.sqlite_version)SQLite version: 3.45.1আপনার সংখ্যাটা আলাদা হতে পারে — আপনার Python কীভাবে বানানো, তার ওপর নির্ভর করে। Python 3.12 বা নতুন সংস্করণে সাধারণত SQLite 3.40 বা তার পরের কোনো সংস্করণ থাকে। কয়েকটা ফিচার নির্দিষ্ট সংস্করণে এসেছে, আর যে পাতায় সেগুলো লাগে সেখানে বলা থাকবে:
| ফিচার | SQLite লাগবে | কোথায় |
|---|---|---|
উইন্ডো ফাংশন (ROW_NUMBER() OVER …) | 3.25+ | অ্যাডভান্সড টিউটোরিয়াল |
CONCAT() ফাংশন | 3.44+ | SELECT: কলাম, এক্সপ্রেশন, অ্যালিয়াস আর DISTINCT |
INSERT/UPDATE/DELETE-এ RETURNING | 3.35+ | INSERT, UPDATE, DELETE আর আপসার্ট, নিরাপদে |
RIGHT JOIN আর FULL OUTER JOIN | 3.39+ | LEFT, RIGHT, FULL, CROSS আর সেলফ জয়েন |
ধাপ ১: একটা প্রজেক্ট ফোল্ডার আর দুটো ফাইল
টিউটোরিয়ালের জন্য একটা ফোল্ডার বানান আর তার ভেতরে কাজ করুন (চাইলে AI-এর জন্য Python-এ বানানো ভার্চুয়াল এনভায়রনমেন্টের ভেতরে):
mkdir sql-for-ai
cd sql-for-aiনিচের দুটো ফাইল হুবহু এই নামে ফোল্ডারটায় সেভ করুন। প্রথমটা, shop.sql, হলো SQL-এ লেখা পুরো ডেটাবেস: টেবিল আগে থেকে থাকলে মুছে দেয়, সাতটা টেবিল তাদের কি আর নিয়মসহ বানায়, তারপর সারি দিয়ে ভরে দেয়। এটা কোনো বদল ছাড়াই SQLite, MySQL 8 আর PostgreSQL-এ চলে। এখন একবার চোখ বুলিয়ে নিন — টেবিলগুলো টেবিল, কি আর রিলেশনশিপ পাতার সেগুলোই, আর এর প্রতিটা স্টেটমেন্ট এই টিউটোরিয়ালেই শিখবেন।
shop.sql
-- BitByte Shop: the practice database for the SQL & Data Handling tutorials.
-- Runs unchanged on SQLite, MySQL 8 and PostgreSQL. Foreign keys are written as table
-- constraints because MySQL silently ignores a REFERENCES written on the column itself.
DROP TABLE IF EXISTS payments;
DROP TABLE IF EXISTS order_items;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS products;
DROP TABLE IF EXISTS categories;
DROP TABLE IF EXISTS customers;
DROP TABLE IF EXISTS employees;
CREATE TABLE customers (
customer_id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
city VARCHAR(50),
joined_on DATE NOT NULL
);
CREATE TABLE categories (
category_id INTEGER PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE products (
product_id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
category_id INTEGER,
price INTEGER NOT NULL CHECK (price > 0),
stock INTEGER CHECK (stock >= 0),
FOREIGN KEY (category_id) REFERENCES categories (category_id)
);
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
order_date DATE NOT NULL,
status VARCHAR(20) NOT NULL
CHECK (status IN ('pending', 'shipped', 'delivered', 'cancelled')),
FOREIGN KEY (customer_id) REFERENCES customers (customer_id)
);
CREATE TABLE order_items (
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price INTEGER NOT NULL,
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders (order_id),
FOREIGN KEY (product_id) REFERENCES products (product_id)
);
CREATE TABLE payments (
payment_id INTEGER PRIMARY KEY,
order_id INTEGER NOT NULL,
amount INTEGER NOT NULL CHECK (amount > 0),
method VARCHAR(20) NOT NULL,
paid_on DATE NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders (order_id)
);
CREATE TABLE employees (
employee_id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
department VARCHAR(50) NOT NULL,
manager_id INTEGER,
salary INTEGER NOT NULL,
hired_on DATE NOT NULL,
FOREIGN KEY (manager_id) REFERENCES employees (employee_id)
);
INSERT INTO customers (customer_id, name, email, city, joined_on) VALUES
(1, 'Nadia Rahman', 'nadia@example.com', 'Dhaka', '2025-11-03'),
(2, 'Tanvir Ahmed', 'tanvir@example.com', 'Chattogram', '2025-12-14'),
(3, 'Farhana Akter', 'farhana@example.com', 'Dhaka', '2026-01-09'),
(4, 'Rafiq Islam', 'rafiq@example.com', 'Sylhet', '2026-01-22'),
(5, 'Sadia Chowdhury', 'sadia@example.com', NULL, '2026-02-05'),
(6, 'Imran Hossain', 'imran@example.com', 'Khulna', '2026-02-18'),
(7, 'Mitu Das', 'mitu@example.com', 'Dhaka', '2026-03-30'),
(8, 'Karim Uddin', 'karim@example.com', 'Rajshahi', '2026-04-11');
INSERT INTO categories (category_id, name) VALUES
(1, 'Books'),
(2, 'Electronics'),
(3, 'Courses'),
(4, 'Accessories'),
(5, 'Furniture');
INSERT INTO products (product_id, name, category_id, price, stock) VALUES
(1, 'Python Crash Course', 1, 1200, 40),
(2, 'Hands-On Machine Learning', 1, 2500, 15),
(3, 'Mechanical Keyboard', 2, 4500, 12),
(4, 'Wireless Mouse', 2, 900, 60),
(5, '27-inch Monitor', 2, 18500, 5),
(6, 'SQL Masterclass', 3, 3000, NULL),
(7, 'AI Engineering Bootcamp', 3, 15000, NULL),
(8, 'Laptop Stand', 4, 1500, 25),
(9, 'USB-C Hub', 4, 2200, 0),
(10, 'Noise-Cancelling Headphones', 2, 7500, 8),
(11, 'Gift Card', NULL, 1000, NULL);
INSERT INTO orders (order_id, customer_id, order_date, status) VALUES
(1, 1, '2026-01-12', 'delivered'),
(2, 2, '2026-01-20', 'delivered'),
(3, 1, '2026-02-03', 'delivered'),
(4, 3, '2026-02-14', 'delivered'),
(5, 4, '2026-02-25', 'cancelled'),
(6, 2, '2026-03-08', 'delivered'),
(7, 5, '2026-03-15', 'delivered'),
(8, 3, '2026-03-28', 'delivered'),
(9, 6, '2026-04-02', 'delivered'),
(10, 1, '2026-04-19', 'delivered'),
(11, 7, '2026-05-06', 'delivered'),
(12, 2, '2026-05-21', 'shipped'),
(13, 6, '2026-06-02', 'pending'),
(14, 3, '2026-06-09', 'delivered');
INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
(1, 1, 1, 1200),
(1, 4, 1, 900),
(2, 3, 1, 4500),
(3, 6, 1, 3000),
(4, 2, 1, 2500),
(4, 8, 1, 1500),
(5, 5, 1, 18500),
(6, 7, 1, 15000),
(7, 4, 2, 900),
(7, 9, 1, 2200),
(8, 1, 2, 1200),
(9, 3, 1, 4050),
(9, 4, 1, 900),
(10, 5, 1, 18500),
(11, 6, 1, 3000),
(11, 2, 1, 2500),
(12, 8, 2, 1500),
(13, 7, 1, 15000),
(14, 9, 1, 2200),
(14, 4, 1, 900);
INSERT INTO payments (payment_id, order_id, amount, method, paid_on) VALUES
(1, 1, 2100, 'bkash', '2026-01-12'),
(2, 2, 4500, 'card', '2026-01-20'),
(3, 3, 3000, 'bkash', '2026-02-03'),
(4, 4, 4000, 'cash', '2026-02-16'),
(5, 6, 5000, 'bkash', '2026-03-08'),
(6, 6, 10000, 'card', '2026-03-10'),
(7, 7, 4000, 'card', '2026-03-15'),
(8, 8, 2400, 'bkash', '2026-03-28'),
(9, 9, 4950, 'card', '2026-04-02'),
(10, 10, 18500, 'card', '2026-04-19'),
(11, 11, 5500, 'bkash', '2026-05-06'),
(12, 12, 3000, 'card', '2026-05-21'),
(13, 14, 3100, 'cash', '2026-06-11');
INSERT INTO employees (employee_id, name, department, manager_id, salary, hired_on) VALUES
(1, 'Ayesha Siddiqua', 'Management', NULL, 250000, '2022-01-10'),
(2, 'Hasan Mahmud', 'Engineering', 1, 180000, '2022-03-01'),
(3, 'Nusrat Jahan', 'Engineering', 2, 120000, '2023-06-15'),
(4, 'Zahid Hasan', 'Engineering', 2, 120000, '2024-02-01'),
(5, 'Lamia Karim', 'Data', 1, 160000, '2022-08-20'),
(6, 'Shuvo Roy', 'Data', 5, 95000, '2025-01-05'),
(7, 'Priya Sen', 'Data', 5, 110000, '2024-09-12');ক্রমটা খেয়াল করুন: মোছার সময় আগে যায় যে টেবিল অন্যের দিকে নির্দেশ করে, পরে যার দিকে নির্দেশ করা হয় (orders-এর আগে payments, customers-এর আগে orders); আর বানানোর সময় ঠিক উল্টো। কোনো টেবিলের দিকে অন্য টেবিলের ফরেন কি নির্দেশ করে থাকলে সার্ভার ডেটাবেস সেটা মুছতে দেয় না, আর যে টেবিল এখনো নেই, ফরেন কি তার দিকে নির্দেশ করতে পারে না।
দ্বিতীয় ফাইল, sqlhelp.py, একটা ছোট হেল্পার, যা পুরো টিউটোরিয়াল জুড়ে কাজে লাগবে:
sqlhelp.py
"""Helpers for the BitByte SQL tutorials: a connection to shop.db, show() and reset()."""
import sqlite3
con = sqlite3.connect("shop.db")
con.execute("PRAGMA foreign_keys = ON") # SQLite checks foreign keys only when asked
def show(sql, params=()):
"""Run one SELECT and print its rows as a table."""
cur = con.execute(sql, params)
cols = [d[0] for d in cur.description]
rows = cur.fetchall()
text = [["NULL" if v is None else str(v) for v in row] for row in rows]
widths = [max([len(c)] + [len(r[i]) for r in text]) for i, c in enumerate(cols)]
def line():
return "+" + "+".join("-" * (w + 2) for w in widths) + "+"
def fmt(cells, values=None):
out = []
for i, (cell, w) in enumerate(zip(cells, widths)):
number = values is not None and isinstance(values[i], (int, float))
out.append(cell.rjust(w) if number else cell.ljust(w))
return "| " + " | ".join(out) + " |"
print(line(), fmt(cols), line(), sep="\n")
for cells, values in zip(text, rows):
print(fmt(cells, values))
print(line())
def reset():
"""Rebuild shop.db from shop.sql: every table back to its starting rows."""
with open("shop.sql", encoding="utf-8") as f:
script = f.read()
con.commit()
con.execute("PRAGMA foreign_keys = OFF") # so tables you made that point at the shop do not block it
try:
con.executescript(script)
finally:
con.execute("PRAGMA foreign_keys = ON")এটা আপনাকে তিনটা জিনিস দেয়:
con—shop.db-এর সাথে একটা খোলা কানেকশন, ফরেন কি যাচাই চালু করা।show(sql, params=())— একটাSELECTচালিয়ে সারিগুলো টেবিল আকারে প্রিন্ট করে; না-থাকা মানের জায়গায়NULLলেখে, আর সংখ্যাগুলো ডান দিক ঘেঁষে বসায়। এই টিউটোরিয়ালের প্রতিটা ফলাফলের টেবিল দেখতে এর আউটপুটের মতো।reset()—shop.sqlআবার চালায়, ফলে প্রতিটা টেবিল শুরুর সারিগুলোতে ফিরে যায়।
ধাপ ২: Python দিয়ে shop.db বানান
ডেটাবেস বানাতে কী কী লাগে, এখানে ধাপে ধাপে লেখা। একই ফোল্ডারে build_db.py নামের একটা ফাইলে এটা রাখুন আর python build_db.py চালান (অথবা নোটবুকের একটা সেলে চালান):
import sqlite3
# 1. কানেক্ট করুন। shop.db না থাকলে SQLite একটা খালি ফাইল বানিয়ে নেয়।
con = sqlite3.connect("shop.db")
# 2. এই কানেকশনে ফরেন কি মানতে SQLite-কে বলুন।
con.execute("PRAGMA foreign_keys = ON")
# 3. shop.sql পড়ে এর প্রতিটা স্টেটমেন্ট চালান।
with open("shop.sql", encoding="utf-8") as f:
con.executescript(f.read())
con.commit()
# 4. যাচাই: প্রতিটা টেবিলে কয়টা সারি ঢুকল?
tables = ["customers", "categories", "products", "orders",
"order_items", "payments", "employees"]
for table in tables:
count = con.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0]
print(f"{table:12} {count:3} rows")
con.close()customers 8 rows
categories 5 rows
products 11 rows
orders 14 rows
order_items 20 rows
payments 13 rows
employees 7 rowsএই সাতটা সংখ্যা দেখলে বুঝবেন ডেটাবেস তৈরি। দুটো খুঁটিনাটি জেনে রাখুন:
execute()চালায় একটা স্টেটমেন্ট;executescript()চালায় সেমিকোলন দিয়ে আলাদা করা অনেক স্টেটমেন্টের পুরো একটা স্ক্রিপ্ট। এ জন্যই ধাপ ৩-এ এটা ব্যবহার হয়েছে।- ধাপ ৪-এর টেবিলের নামগুলো আমাদের নিজেদের লেখা নির্দিষ্ট স্ট্রিং, তাই এখানে f-string দিয়ে SQL-এ বসানো নিরাপদ। ব্যবহারকারীর কাছ থেকে আসা মান হলে কথা আলাদা — সেগুলোর জন্য
?প্লেসহোল্ডার ব্যবহার করবেন, নিচে দেখানো আছে।
sqlhelp.py-এর reset() হেল্পারের আগে থেকে খোলা কানেকশনেই ১–৩ ধাপের কাজটা করে। তাই এখন থেকে শুধু সেটাকে ডাকলেই চলবে।
ধাপ ৩: আপনার প্রথম কোয়েরি
shop.db, shop.sql আর sqlhelp.py এক ফোল্ডারে রেখে হেল্পারটা ইমপোর্ট করুন আর একটা প্রশ্ন করুন — সবচেয়ে দামি তিনটা প্রোডাক্ট:
from sqlhelp import con, show
show("SELECT product_id, name, price FROM products ORDER BY price DESC LIMIT 3")+------------+-----------------------------+-------+
| product_id | name | price |
+------------+-----------------------------+-------+
| 5 | 27-inch Monitor | 18500 |
| 7 | AI Engineering Bootcamp | 15000 |
| 10 | Noise-Cancelling Headphones | 7500 |
+------------+-----------------------------+-------+কোয়েরির কোনো অংশ যখন একটা ভেরিয়েবল থেকে আসে, সেটা স্ট্রিংয়ের ভেতরে জুড়ে না দিয়ে ? প্লেসহোল্ডার দিয়ে আলাদা করে পাঠান। ডেটাবেস তখন সেটাকে কঠোরভাবে শুধু একটা মান হিসেবে ধরে, যা আপনাকে SQL ইনজেকশন থেকেও বাঁচায় (অ্যাডভান্সড টিউটোরিয়ালে আছে):
city = "Dhaka"
show("SELECT customer_id, name, city FROM customers WHERE city = ? ORDER BY customer_id", (city,))+-------------+---------------+-------+
| customer_id | name | city |
+-------------+---------------+-------+
| 1 | Nadia Rahman | Dhaka |
| 3 | Farhana Akter | Dhaka |
| 7 | Mitu Das | Dhaka |
+-------------+---------------+-------+show() শুধু দেখার জন্য। আসল কোডে সারিগুলো লাগবে Python-এর মান হিসেবে, বা বিশ্লেষণ কিংবা মডেলের জন্য তৈরি একটা pandas DataFrame হিসেবে:
import pandas as pd
rows = con.execute("SELECT name, price FROM products ORDER BY price DESC LIMIT 2").fetchall()
print(rows)
df = pd.read_sql("SELECT product_id, name, price, stock FROM products ORDER BY product_id LIMIT 4", con)
print(df)[('27-inch Monitor', 18500), ('AI Engineering Bootcamp', 15000)]
product_id name price stock
0 1 Python Crash Course 1200 40
1 2 Hands-On Machine Learning 2500 15
2 3 Mechanical Keyboard 4500 12
3 4 Wireless Mouse 900 60সারিগুলো ফেরত আসে টাপলের একটা লিস্ট হিসেবে; pd.read_sql এক লাইনেই একটা কোয়েরিকে DataFrame বানিয়ে দেয়, আর সেখানে SQL-এর NULL হয়ে যায় NaN বা None। অ্যাডভান্সড টিউটোরিয়ালে এটা প্রচুর ব্যবহার করবেন।
ধাপ ৪: যখন খুশি রিসেট করুন
যে পাতাগুলো ডেটা বদলায় (INSERT, UPDATE, DELETE, DROP), সেগুলো ধরে নেয় আপনি শুরুর সারিগুলো থেকে শুরু করছেন। এখানে ইচ্ছে করে সব পেমেন্ট মুছে আবার ফিরিয়ে আনছি:
from sqlhelp import con, show, reset
con.execute("DELETE FROM payments")
con.commit()
show("SELECT COUNT(*) AS payments FROM payments")
reset()
show("SELECT COUNT(*) AS payments FROM payments")+----------+
| payments |
+----------+
| 0 |
+----------+
+----------+
| payments |
+----------+
| 13 |
+----------+এটা অভ্যাস বানিয়ে ফেলুন: প্রতিটা পাতা শুরু করুন reset() দিয়ে, তাহলে আপনার ফলাফল পাতায় ছাপা ফলাফলের সাথে মিলবে। (shop.db মুছে আবার build_db.py চালালেও একই কাজ হয়।)
Python ছাড়া: DB Browser for SQLite
DB Browser for SQLite হলো Windows, macOS আর Linux-এর জন্য একটা বিনামূল্যের গ্রাফিক্যাল টুল — ডেটাবেস চোখে দেখার সবচেয়ে সহজ উপায়। ইনস্টল করার পর:
- Open Database চেপে
shop.dbবেছে নিন। (এখনোshop.dbনেই? File → Import → Database from SQL file… দিয়েshop.sqlবেছে নিন, আর নতুন ডেটাবেসটাshop.dbনামে সেভ করুন।) - Database Structure ট্যাবে টেবিল আর তাদের কলামের তালিকা।
- Browse Data একটা টেবিলের সারিগুলো স্প্রেডশিটের মতো দেখায়।
- Execute SQL-এ কোয়েরি লিখবেন; চালাতে Ctrl+Return (বা F5) চাপুন, ফলাফল নিচে আসবে।
DB Browser আপনার বদলগুলো একটা খোলা ট্রানজ্যাকশনে ধরে রাখে, যতক্ষণ না Write Changes (বা Revert Changes) চাপছেন। বদল ঝুলে থাকা অবস্থায় অন্য কোনো প্রোগ্রাম — আপনার Python স্ক্রিপ্টসহ — কিছু লিখতে গেলে পায়
database is locked। Python-এ ফেরার আগে লিখে ফেলুন বা বাতিল করুন।
Python ছাড়া: sqlite3 শেল
sqlite3 কমান্ড-লাইন শেল একটাই ছোট প্রোগ্রাম। macOS-এ এটা আগে থেকেই থাকে; Linux-এ প্যাকেজ ম্যানেজার দিয়ে ইনস্টল করুন (sudo apt install sqlite3); Windows-এ sqlite.org/download.html থেকে "sqlite-tools" zip নামিয়ে sqlite3.exe আপনার প্রজেক্ট ফোল্ডারে আনজিপ করুন। তারপর:
sqlite3 shop.dbশেলের ভেতরে ডট দিয়ে শুরু হওয়া লাইনগুলো শেলের নিজস্ব কমান্ড; বাকি সব SQL, আর সেগুলো ; দিয়ে শেষ করতে হয়:
sqlite> .read shop.sql
sqlite> .tables
sqlite> .headers on
sqlite> .mode box
sqlite> PRAGMA foreign_keys = ON;
sqlite> SELECT name, price FROM products ORDER BY price DESC LIMIT 3;
sqlite> .quit.read একটা স্ক্রিপ্ট চালায় (এখানে reset()-এর মতো ডেটাবেসটা আবার বানায়), .tables টেবিলের তালিকা দেখায়, আর .headers on ও .mode box মিলে ফলাফল দাগ-টানা টেবিলে দেখায়। Python-এর মতো শেলও ফরেন কি বন্ধ রাখে, যতক্ষণ না আপনি চালু করেন। টার্মিনাল থেকে সরাসরি একটা কোয়েরিও চালানো যায়: sqlite3 shop.db "SELECT COUNT(*) FROM orders;"।
ঐচ্ছিক: MySQL বা PostgreSQL
এই টিউটোরিয়ালের জন্য SQLite-ই যথেষ্ট। তবে প্রোডাকশন সিস্টেম সাধারণত সার্ভার ডেটাবেসে চলে, আর যেখানে MySQL ও PostgreSQL আলাদা আচরণ করে, পাতাগুলো সেখানে সেগুলো দেখায়। সেই ব্লকগুলো নিজে চালাতে চাইলে দুটো পথ আছে:
- সরাসরি ইনস্টল: MySQL Community Server dev.mysql.com/downloads থেকে; PostgreSQL postgresql.org/download থেকে (Windows-এ EDB ইনস্টলার, macOS-এ Postgres.app বা Homebrew, Linux-এ প্যাকেজ ম্যানেজার)।
- Docker ব্যবহার (আরও সহজ, আর কাজ শেষে মুছে ফেলাও সহজ): প্রতিটা সার্ভারের জন্য একটা কমান্ড।
# পোর্ট 3306-এ MySQL 8, shop নামের একটা খালি ডেটাবেসসহ
docker run --name shop-mysql -e MYSQL_ROOT_PASSWORD=course -e MYSQL_DATABASE=shop \
-p 3306:3306 -d mysql:8.4
# পোর্ট 5432-এ pgvector এক্সটেনশনসহ PostgreSQL, ডেটাবেস shop
docker run --name shop-postgres -e POSTGRES_PASSWORD=course -e POSTGRES_DB=shop \
-p 5432:5432 -d pgvector/pgvector:pg17
# চালু হতে কয়েক সেকেন্ড অপেক্ষা করুন, তারপর দুটোতেই একই shop.sql লোড করুন
docker exec -i shop-mysql mysql -uroot -pcourse shop < shop.sql
docker exec -i shop-postgres psql -U postgres -d shop < shop.sql
# একটা ইন্টারঅ্যাকটিভ প্রম্পট খুলুন (বের হতে exit বা \q লিখুন)
docker exec -it shop-mysql mysql -uroot -pcourse shop
docker exec -it shop-postgres psql -U postgres -d shopপ্রথমবার PostgreSQL প্রতিটা DROP TABLE IF EXISTS-এর জন্য একটা NOTICE: table "payments" does not exist, skipping দেখায় — এতে ক্ষতি নেই; MySQL সতর্ক করে যে কমান্ড লাইনে পাসওয়ার্ড দেওয়া অনিরাপদ — অনুশীলনের কন্টেইনারে চলে, আসল সার্ভারে কখনো নয়। সার্ভারের ডেটাবেস রিসেট করতে শুধু shop.sql আবার লোড করুন। ডেটা ঠিকমতো এসেছে কি না দেখতে দুটোতেই একই কোয়েরি চালান:
-- MySQL
SELECT COUNT(*) AS orders, MAX(order_date) AS last_order FROM orders;+--------+------------+
| orders | last_order |
+--------+------------+
| 14 | 2026-06-09 |
+--------+------------+-- PostgreSQL
SELECT COUNT(*) AS orders, MAX(order_date) AS last_order FROM orders;+--------+------------+
| orders | last_order |
+--------+------------+
| 14 | 2026-06-09 |
+--------+------------+একটা গ্রাফিক্যাল ক্লায়েন্ট থাকলে সার্ভারে কাজ করা অনেক আরামের:
| ক্লায়েন্ট | যেগুলোর সাথে চলে | মন্তব্য |
|---|---|---|
| DBeaver (Community) | SQLite, MySQL, PostgreSQL, SQL Server আর আরও অনেক | বিনামূল্যে; এই কোর্সের সবকিছুর জন্য একটাই টুল |
| MySQL Workbench | MySQL | বিনামূল্যে, Oracle-এর; স্কিমার ডায়াগ্রামও আঁকে |
| pgAdmin | PostgreSQL | বিনামূল্যে, PostgreSQL-এর অফিশিয়াল GUI; ব্রাউজারের ট্যাবে চলে |
| DB Browser for SQLite | SQLite | বিনামূল্যে আর খুব সহজ |
| VS Code এক্সটেনশন (যেমন SQLTools) | বেশিরভাগ ডেটাবেস | যাঁরা সারাক্ষণ VS Code-এই কাজ করেন, তাঁদের জন্য সুবিধাজনক |
ওপরের Docker কন্টেইনারের কানেকশন সেটিং: হোস্ট localhost, পোর্ট 3306 (MySQL) বা 5432 (PostgreSQL), ইউজার root / postgres, পাসওয়ার্ড course, ডেটাবেস shop। Python থেকে এগুলোর সাথে কানেক্ট করা (PyMySQL, psycopg) আছে অ্যাডভান্সড টিউটোরিয়ালে।
সাধারণ ভুল
- অন্য ফোল্ডার থেকে চালানো।
sqlite3.connect("shop.db")ফাইলটা খোঁজে বর্তমান ফোল্ডারে, আর না পেলে চুপচাপ একটা খালি ডেটাবেস বানিয়ে ফেলে। তখন প্রথম কোয়েরিই "no such table" দিয়ে ব্যর্থ হয়:import os import sqlite3 os.makedirs("elsewhere", exist_ok=True) empty = sqlite3.connect("elsewhere/shop.db") # ভুল ফোল্ডার: নতুন, খালি একটা ফাইল try: empty.execute("SELECT * FROM products") except sqlite3.OperationalError as error: print("Error:", error) print("size of the new file:", os.path.getsize("elsewhere/shop.db"), "bytes") empty.close()সমাধান: স্ক্রিপ্টটা প্রজেক্ট ফোল্ডার থেকে চালান (Error: no such table: products size of the new file: 0 bytesprint(os.getcwd())দিয়ে দেখে নিন), অথবা স্ক্রিপ্টের নিজের অবস্থান থেকে পাথ বানান:Path(__file__).parent / "shop.db"। - পুরো স্ক্রিপ্টের জন্য
execute()ব্যবহার। এটা একটাই স্টেটমেন্ট চালায়:import sqlite3 con2 = sqlite3.connect("shop.db") try: con2.execute("DELETE FROM payments; DELETE FROM orders;") except sqlite3.ProgrammingError as error: print("Error:", error) con2.close()সমাধান: স্ক্রিপ্টের জন্যError: You can only execute one statement at a time.executescript(), ধাপ ২-এর মতো। commit()ভুলে যাওয়া।INSERT,UPDATEবাDELETE-এর আগে Python-এরsqlite3একটা ট্রানজ্যাকশন খোলে। কমিট না করে কানেকশন বন্ধ করলে বদলটা ফেলে দেওয়া হয়:import sqlite3 w = sqlite3.connect("shop.db") w.execute("INSERT INTO categories (category_id, name) VALUES (6, 'Toys')") w.close() # কমিট নেই: ইনসার্টটা বাতিল r = sqlite3.connect("shop.db") print("categories:", r.execute("SELECT COUNT(*) FROM categories").fetchone()[0]) r.close()সমাধান: যে বদল রাখতে চান তার পরcategories: 5con.commit()ডাকুন (অথবাwith con:ব্যবহার করুন, যা আপনার হয়ে কমিট করে)।- অন্য প্রোগ্রামে বদল কমিট না করে ফেলে রাখা। DB Browser (বা অন্য কোনো স্ক্রিপ্ট) কমিট না করা বদল ধরে রাখলে অন্য জায়গার লেখার চেষ্টা অপেক্ষা করে, তারপর ব্যর্থ হয়:
import sqlite3 a = sqlite3.connect("shop.db") a.execute("UPDATE products SET stock = stock WHERE product_id = 1") # ট্রানজ্যাকশন খোলা রইল b = sqlite3.connect("shop.db", timeout=0) # অপেক্ষা করবে না try: b.execute("UPDATE products SET stock = stock WHERE product_id = 2") except sqlite3.OperationalError as error: print("Error:", error) a.rollback() a.close() b.close()সমাধান: দ্রুত কমিট বা রোলব্যাক করুন, আর DB Browser-এ Write Changes বা Revert Changes চাপুন।Error: database is locked
নিজে চেষ্টা করুন
- সহজ: Python-এর বদলে একটা কোয়েরি দিয়ে SQLite-এর সংস্করণ জিজ্ঞেস করুন (
sqlite_version()), আরshow()দিয়েemployees-এর সারি গুনুন। - মাঝারি:
?প্লেসহোল্ডারসহshow()দিয়ে ক্যাটাগরি ২ (Electronics)-এর প্রতিটা প্রোডাক্টেরproduct_id,nameআরpriceদেখান,product_idধরে সাজিয়ে। - কঠিন: একটা ফাংশন
table_sizes()লিখুন, যা SQLite-এর নিজস্ব তালিকাsqlite_masterথেকে টেবিলের নামগুলো পড়ে প্রতিটা টেবিলের সারির সংখ্যা প্রিন্ট করে।order_items-এর সারিগুলো মুছে এটা চালান, তারপরreset()করে আবার চালান।
উত্তর
১. sqlite_version() একটা SQL ফাংশন; গণনাটা সাধারণ একটা কোয়েরি:
from sqlhelp import show
show("SELECT sqlite_version() AS version")
show("SELECT COUNT(*) AS employees FROM employees")২. মানটা যায় টাপলে, স্ট্রিংয়ের ভেতরে নয়:
show("SELECT product_id, name, price FROM products WHERE category_id = ? ORDER BY product_id", (2,))৩. SQLite যত টেবিল চেনে, প্রতিটা sqlite_master-এর একটা সারি:
from sqlhelp import con, reset
def table_sizes():
names = [row[0] for row in con.execute(
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")]
for name in names:
count = con.execute(f"SELECT COUNT(*) FROM {name}").fetchone()[0]
print(f"{name:12} {count}")
con.execute("DELETE FROM order_items")
con.commit()
table_sizes()
reset()
print("--- after reset")
table_sizes()categories 5
customers 8
employees 7
order_items 0
orders 14
payments 13
products 11
--- after reset
categories 5
customers 8
employees 7
order_items 20
orders 14
payments 13
products 11সারসংক্ষেপ
- SQLite Python-এর সাথেই আসে (
import sqlite3);sqlite3.sqlite_versionবলে দেয় কোন ফিচারগুলো পাবেন। shop.sqlপুরো দোকানটা বানায়;executescript()সেটা চালায়, আরsqlhelp.py-এরreset()যেকোনো সময় আপনার হয়ে কাজটা করে।show()একটা কোয়েরির ফলাফল টেবিল আকারে দেখায়; মানের জন্য?প্লেসহোল্ডার ব্যবহার করুন, আর Python-এ ডেটা লাগলেfetchall()বাpd.read_sql।- DB Browser for SQLite আর
sqlite3শেল একই ফাইলে কাজ করে; বদলগুলো কমিট করুন বা লিখে ফেলুন, যাতে অন্য প্রোগ্রাম আটকে না যায়। - MySQL আর PostgreSQL মাত্র একটা
docker runদূরে, একইshop.sqlলোড করে, আর DBeaver, MySQL Workbench বা pgAdmin দিয়ে ব্যবহার করা সবচেয়ে সহজ।
এরপর: SQL কীভাবে কাজ করে: স্টেটমেন্ট, কমান্ডের ধরন আর কোয়েরির ক্রম পাতায় দেখবেন ভাষাটার নিজের গড়ন — স্টেটমেন্ট, কিওয়ার্ড, উদ্ধৃতিচিহ্ন, কমেন্ট, কমান্ডের পরিবার, আর ডেটাবেস আসলে কোন ক্রমে একটা কোয়েরির অংশগুলো চালায়।