অধ্যায় 1 · ডেটাবেস ও SQL
টেবিল, কি আর রিলেশনশিপ
- পৃষ্ঠা 2 / 22
- 13 মিনিট পড়া
একটা রিলেশনাল ডেটাবেস হলো কয়েকটা টেবিল, যারা একে অপরের দিকে নির্দেশ করে। এই একটা ধারণাই — কি দিয়ে যুক্ত টেবিল — আপনাকে চারটা আলাদা জায়গায় থাকা ডেটা মিলিয়ে প্রশ্ন করতে দেয়: "ঢাকার প্রত্যেক কাস্টমার ইলেকট্রনিক্সে কত খরচ করেছেন?" আর এ কারণেই ডেটাবেস ভুল ডেটা ঢুকতে না দিয়ে আটকে দিতে পারে: যে কাস্টমার নেই, তার নামে অর্ডার সেভই করা যায় না।
এই পাতায় BitByte Shop ডেটাবেসটা খুলে দেখবেন — টিউটোরিয়ালের বাকি অংশ জুড়ে এটাই ব্যবহার করবেন — আর এটা কীভাবে বানানো তা বুঝবেন। এখনো shop.db না বানিয়ে থাকলে পরের পাতা, সেটআপ: আপনার অনুশীলনের ডেটাবেস, দুই মিনিটে সেটা করে দেবে; আগে এই পাতাটা পড়ে নিলেও সমস্যা নেই।
যা শিখবেন
- টেবিল, সারি (row) আর কলাম কী, আর pandas DataFrame-এর সাথে এদের মিল-অমিল
- প্রাইমারি কি, কম্পোজিট কি আর ফরেন কি, আর ডেটাবেস কীভাবে এগুলো মানতে বাধ্য করে
- ওয়ান-টু-ওয়ান, ওয়ান-টু-মেনি আর মেনি-টু-মেনি রিলেশনশিপ, আর জাংশন টেবিল কী
- দোকানের স্কিমা কীভাবে পড়বেন, আর কাস্টমারের তথ্য কেন প্রতিটা অর্ডারে কপি করা হয় না (নরমালাইজেশনের প্রথম পরিচয়)
টেবিল, সারি আর কলাম
একটা টেবিল-এ থাকে এক ধরনের জিনিস: কাস্টমার, প্রোডাক্ট, অর্ডার। প্রতিটা সারি সেই ধরনের একটা জিনিস, আর প্রতিটা কলাম তার সম্পর্কে একটা তথ্য, যার নাম আর টাইপ নির্দিষ্ট। এই হলো পুরো customers টেবিল:
SELECT * FROM customers ORDER BY customer_id;+-------------+-----------------+---------------------+------------+------------+
| customer_id | name | email | city | joined_on |
+-------------+-----------------+---------------------+------------+------------+
| 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 |
+-------------+-----------------+---------------------+------------+------------+৮টা সারি, ৫টা কলাম। কাস্টমার ৫-এর city-তে আছে NULL: SQL-এ NULL মানে "অজানা / নেই" — "NULL" লেখাটা নয়, ফাঁকা স্ট্রিংও নয়। এর সাথে প্রায়ই দেখা হবে।
কে বলছেন তার ওপর নির্ভর করে একই জিনিসের আলাদা আলাদা নাম চলে:
| ডেটাবেসে | তাত্ত্বিক নাম | pandas / ML-এ |
|---|---|---|
| table (টেবিল) | relation | DataFrame, ডেটাসেট |
| row (সারি) | tuple, record | row, example, sample |
| column (কলাম) | attribute, field | column, feature |
টেবিল দেখতে DataFrame-এর মতো, কিন্তু আসল পার্থক্য আছে। টেবিল থাকে ডিস্কে, তাই মেমোরির চেয়ে অনেক বড় হতে পারে; প্রতিটা কলামের টাইপ আগে থেকে ঘোষণা করা; ডেটাবেস "কখনো ফাঁকা নয়" বা "অবশ্যই অনন্য" ধরনের নিয়ম মানতে বাধ্য করে; আর টেবিলের সারিগুলোর নিজস্ব কোনো ক্রম নেই — এ জন্যই এখানে প্রতিটা কোয়েরির শেষে ORDER BY থাকে।
প্রাইমারি কি: এক সারি, এক পরিচয়
প্রাইমারি কি (primary key, PK) হলো সেই কলাম বা কলামের সেট, যা প্রতিটা সারিকে আলাদা করে চেনায়। এটা অবশ্যই অনন্য আর কখনো NULL নয়, আর বাস্তবে কখনো বদলানো উচিত নয়। customers-এ এটা হলো customer_id। দ্বিতীয় আরেকজন ১ নম্বর কাস্টমার যোগ করার চেষ্টা করুন, ডেটাবেস রাজি হবে না:
INSERT INTO customers (customer_id, name, email, city, joined_on)
VALUES (1, 'Nadia Rahman', 'nadia2@example.com', 'Dhaka', '2026-07-01');Error: UNIQUE constraint failed: customers.customer_idকাস্টমারের নাম বা ইমেইল না নিয়ে একটা সংখ্যা কেন? একই নাম অনেকের থাকে — বাংলাদেশে নাদিয়া রহমান অনেকজন — আর ইমেইল বদলায়। অর্থহীন একটা পূর্ণসংখ্যা (সারোগেট কি) কখনো বদলাতে হয় না, তাই এর দিকে নির্দেশ করা কিছুই কখনো ভাঙে না। ইমেইলটা তবু নিজের UNIQUE নিয়মে সুরক্ষিত থাকে।
একটা কি-তে একাধিক কলামও থাকতে পারে: কম্পোজিট কি। order_items-এ কি হলো (order_id, product_id): একটা অর্ডারে অনেক প্রোডাক্ট থাকতে পারে, একটা প্রোডাক্ট অনেক অর্ডারে থাকতে পারে, কিন্তু একটা অর্ডারে প্রতিটা প্রোডাক্ট থাকে একবারই (দুটো মাউস কেনা মানে quantity = 2, দুটো সারি নয়)।
ফরেন কি: টেবিলের মধ্যে সংযোগ
ফরেন কি (foreign key, FK) এমন একটা কলাম, যার প্রতিটা মান অন্য টেবিলে প্রাইমারি কি হিসেবে থাকতেই হবে। orders.customer_id হলো customers.customer_id-এর দিকে একটা ফরেন কি, তাই প্রতিটা অর্ডার বলে দেয় কোন কাস্টমার সেটা দিয়েছেন — কাস্টমারের তথ্য কপি না করেই:
SELECT order_id, customer_id, order_date, status
FROM orders
WHERE customer_id = 1
ORDER BY order_id;+----------+-------------+------------+-----------+
| order_id | customer_id | order_date | status |
+----------+-------------+------------+-----------+
| 1 | 1 | 2026-01-12 | delivered |
| 3 | 1 | 2026-02-03 | delivered |
| 10 | 1 | 2026-04-19 | delivered |
+----------+-------------+------------+-----------+(WHERE শুধু মিলে যাওয়া সারিগুলো রাখে; এর জন্য আলাদা পাতা আছে, সারি বাছাই: WHERE, AND, OR ও NOT।) এবার নিয়মটা কাজ করতে দেখুন — ৪২ নম্বর কাস্টমারের নামে অর্ডার, যিনি আসলে নেই:
INSERT INTO orders (order_id, customer_id, order_date, status)
VALUES (99, 42, '2026-07-01', 'pending');Error: FOREIGN KEY constraint failedউল্টো দিকেও একই সুরক্ষা: অর্ডারগুলো যতক্ষণ কাস্টমার ১-এর দিকে নির্দেশ করছে, ততক্ষণ তাঁকে মুছতে পারবেন না।
DELETE FROM customers WHERE customer_id = 1;Error: FOREIGN KEY constraint failedএকে বলে রেফারেনশিয়াল ইন্টেগ্রিটি: প্রতিটা রেফারেন্স কোনো আসল জিনিসের দিকে নির্দেশ করে। PostgreSQL আর MySQL (ডিফল্ট InnoDB ইঞ্জিনে) শুরু থেকেই ফরেন কি মানতে বাধ্য করে, আর তাদের মেসেজ আরও নির্দিষ্ট:
-- PostgreSQL
INSERT INTO orders (order_id, customer_id, order_date, status)
VALUES (99, 42, '2026-07-01', 'pending');Error: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"SQLite ফরেন কি যাচাই করে শুধু চালু করলে, আর প্রতিটা কানেকশনের জন্য আলাদাভাবে,
PRAGMA foreign_keys = ONদিয়ে। কোর্সের হেল্পারsqlhelp.pyএটা আপনার হয়ে করে দেয়; নিজে কোনো ডেটাবেস খুললে আপনাকেও এটা করতে হবে (নিচে সাধারণ ভুল দেখুন)।
রিলেশনশিপ
কি দিয়ে তিন ধরনের রিলেশনশিপ তৈরি হয়। প্রশ্নটা এভাবে করুন: "একটা X-এর কয়টা Y?"
| রিলেশনশিপ | মানে | দোকানে | কীভাবে বানানো |
|---|---|---|---|
| ওয়ান-টু-মেনি | বাঁ দিকে একটা সারি, ডান দিকে যেকোনো সংখ্যক | একজন কাস্টমারের অনেক অর্ডার; একটা ক্যাটাগরির অনেক প্রোডাক্ট; একজন ম্যানেজারের অনেক কর্মী | "মেনি" দিকে একটা FK কলাম (orders.customer_id) |
| মেনি-টু-মেনি | দুই দিকেই অনেক | একটা অর্ডারে অনেক প্রোডাক্ট; একটা প্রোডাক্ট অনেক অর্ডারে | দুটো FK-সহ একটা জাংশন টেবিল: order_items |
| ওয়ান-টু-ওয়ান | প্রতিটা দিকে বড়জোর একটা | দোকানে নেই; যেমন কাস্টমারপ্রতি একটা সারির customer_profiles টেবিল | এমন FK, যা একই সাথে PK (বা UNIQUE) |
অনুমান নয়, ডেটা যাচাই করুন
অর্ডার আর পেমেন্ট দেখে মনে হয় ওয়ান-টু-ওয়ান: এক অর্ডার, এক পেমেন্ট। ডেটা অন্য কথা বলে:
SELECT payment_id, order_id, amount, method, paid_on
FROM payments
WHERE order_id IN (5, 6, 13)
ORDER BY payment_id;+------------+----------+--------+--------+------------+
| payment_id | order_id | amount | method | paid_on |
+------------+----------+--------+--------+------------+
| 5 | 6 | 5000 | bkash | 2026-03-08 |
| 6 | 6 | 10000 | card | 2026-03-10 |
+------------+----------+--------+--------+------------+অর্ডার ৬-এর দাম দুই কিস্তিতে দেওয়া হয়েছে, আর অর্ডার ৫ (বাতিল) ও অর্ডার ১৩ (পেন্ডিং)-এর কোনো পেমেন্টই নেই। তাই সম্পর্কটা হলো এক অর্ডারের শূন্য, এক বা একাধিক পেমেন্ট। টেবিল মেলানোর মুহূর্তেই এটা গুরুত্বপূর্ণ হয়ে ওঠে: এখানে ওয়ান-টু-ওয়ান ধরে নিলে আয়ের হিসাবে বা ট্রেনিং সেটে অর্ডার ৬ দুবার গোনা হবে (অনেক টেবিল জয়েন, আর ডুপ্লিকেট সারির ফাঁদ পাতায় ঠিক এটাই দেখানো হয়েছে)।
জাংশন টেবিল
order_items একটা মেনি-টু-মেনি সম্পর্ককে দুটো ওয়ান-টু-মেনি সম্পর্কে ভাগ করে। এই হলো Wireless Mouse (প্রোডাক্ট ৪)-এর প্রতিটা বিক্রি:
SELECT order_id, product_id, quantity, unit_price
FROM order_items
WHERE product_id = 4
ORDER BY order_id;+----------+------------+----------+------------+
| order_id | product_id | quantity | unit_price |
+----------+------------+----------+------------+
| 1 | 4 | 1 | 900 |
| 7 | 4 | 2 | 900 |
| 9 | 4 | 1 | 900 |
| 14 | 4 | 1 | 900 |
+----------+------------+----------+------------+জাংশন টেবিল জোড়াটার নিজস্ব তথ্যও রাখতে পারে: quantity, আর unit_price — বিক্রির সময়ের দাম। অর্ডার ৯-এ কিবোর্ডটা কেনা হয়েছিল ৪০৫০ টাকায়, যদিও তালিকার দাম ৪৫০০। order_items যদি শুধু products.price-এর দিকে নির্দেশ করত, তাহলে দাম বদলালেই আগের সব অর্ডার চুপচাপ বদলে যেত। ML-এর বেলায় তফাতটা এখানেই: সঠিক ট্রেনিং সেট, নাকি এমন সেট যেখানে আজকের দাম গত বছরের উদাহরণে ঢুকে পড়ে (লিকেজ)।
দোকানের স্কিমা
স্কিমা হলো ডেটাবেসের নকশা: এর টেবিল, কলাম, টাইপ, কি আর নিয়ম। এই হলো BitByte Shop-এর স্কিমা; প্রতিটা সংযোগে "1" দিক আর "many" দিক চিহ্নিত (ফরেন কি থাকে many দিকে):
customers (customer_id PK, name, email UNIQUE, city, joined_on)
│ 1
▼ many
orders (order_id PK, customer_id FK, order_date, status)
│ 1 │ 1
▼ many ▼ many
order_items (order_id FK, product_id FK, payments (payment_id PK, order_id FK,
quantity, unit_price) amount, method, paid_on)
PK = (order_id, product_id)
▲ many
│ 1
products (product_id PK, name, category_id FK, price, stock)
▲ many
│ 1
categories (category_id PK, name UNIQUE)
employees (employee_id PK, name, department, manager_id FK, salary, hired_on)
manager_id আবার employees-এর দিকেই নির্দেশ করে: প্রত্যেক কর্মীর 0 বা 1 জন ম্যানেজারডেটাবেসকেও সরাসরি জিজ্ঞেস করা যায়। SQLite-এ PRAGMA table_info একটা টেবিলের কলামগুলো দেখায়, আর PRAGMA foreign_key_list দেখায় তার সংযোগগুলো (MySQL-এ আছে DESCRIBE orders; PostgreSQL-এর psql-এ \d orders):
PRAGMA table_info(orders);
PRAGMA foreign_key_list(order_items);+-----+-------------+-------------+---------+------------+----+
| cid | name | type | notnull | dflt_value | pk |
+-----+-------------+-------------+---------+------------+----+
| 0 | order_id | INTEGER | 0 | NULL | 1 |
| 1 | customer_id | INTEGER | 1 | NULL | 0 |
| 2 | order_date | DATE | 1 | NULL | 0 |
| 3 | status | VARCHAR(20) | 1 | NULL | 0 |
+-----+-------------+-------------+---------+------------+----+
+----+-----+----------+------------+------------+-----------+-----------+-------+
| id | seq | table | from | to | on_update | on_delete | match |
+----+-----+----------+------------+------------+-----------+-----------+-------+
| 0 | 0 | products | product_id | product_id | NO ACTION | NO ACTION | NONE |
| 1 | 0 | orders | order_id | order_id | NO ACTION | NO ACTION | NONE |
+----+-----+----------+------------+------------+-----------+-----------+-------+table_info-তে notnull = 1 মানে কলামটা NULL হতে পারবে না, আর pk প্রাইমারি কি চিহ্নিত করে। (order_id-এ notnull = 0 দেখাচ্ছে, কারণ SQLite-এ INTEGER PRIMARY KEY হলো সারির নিজস্ব id: এটা কখনো NULL হতে পারে না, আর NULL ঢোকালে SQLite নিজেই পরের নম্বরটা বসিয়ে দেয়।) foreign_key_list দেখায় যে order_items.product_id নির্দেশ করে products.product_id-এর দিকে, আর order_items.order_id নির্দেশ করে orders.order_id-এর দিকে।
নরমালাইজেশনের প্রথম পরিচয়
অনেক দোকান Excel-এ একটা বড় শিট রাখে, যার প্রতিটা অর্ডারের লাইনে কাস্টমারের তথ্যও লেখা থাকে। এভাবে রাখলে সমস্যা কী? নাদিয়ার তিনটা অর্ডারের জন্য এমন একটা ফ্ল্যাট টেবিল বানিয়ে দেখা যাক, তিনি ইমেইল বদলালে কী হয়:
CREATE TABLE orders_flat (
order_id INTEGER PRIMARY KEY,
order_date DATE,
customer_name VARCHAR(100),
customer_email VARCHAR(100),
customer_city VARCHAR(50)
);
INSERT INTO orders_flat VALUES
(1, '2026-01-12', 'Nadia Rahman', 'nadia@example.com', 'Dhaka'),
(3, '2026-02-03', 'Nadia Rahman', 'nadia@example.com', 'Dhaka'),
(10, '2026-04-19', 'Nadia Rahman', 'nadia@example.com', 'Dhaka');
-- নাদিয়ার নতুন ইমেইল বদলানো হলো শুধু তাঁর শেষ অর্ডারে
UPDATE orders_flat SET customer_email = 'nadia.r@example.com' WHERE order_id = 10;
SELECT order_id, customer_name, customer_email FROM orders_flat ORDER BY order_id;+----------+---------------+---------------------+
| order_id | customer_name | customer_email |
+----------+---------------+---------------------+
| 1 | Nadia Rahman | nadia@example.com |
| 3 | Nadia Rahman | nadia@example.com |
| 10 | Nadia Rahman | nadia.r@example.com |
+----------+---------------+---------------------+কোন ইমেইলটা ঠিক? টেবিলটা এখন নিজেই নিজের বিরুদ্ধে কথা বলছে — একে বলে আপডেট অ্যানোমালি। ফ্ল্যাট নকশার আরও দুটো সমস্যা আছে: যে কাস্টমার এখনো অর্ডার দেননি (করিম, কাস্টমার ৮), তাঁকে একটা ভুয়া অর্ডার না বানিয়ে লিখে রাখা যায় না (ইনসার্ট অ্যানোমালি); আর কারও একমাত্র অর্ডার মুছলে তাঁর সম্পর্কে জানা সবকিছুই মুছে যায় (ডিলিট অ্যানোমালি)।
দোকানের আসল নকশা প্রতিটা তথ্য রাখে একবার: ইমেইল থাকে customers-এর একটা সারিতে, আর অর্ডারগুলো customer_id দিয়ে সেদিকে নির্দেশ করে। একটা সারিতে একটা UPDATE করলেই সব জায়গায় ঠিক হয়ে যায়। ডেটাকে এভাবে ভাগ করাকে বলে নরমালাইজেশন; অ্যাডভান্সড টিউটোরিয়ালে এর আনুষ্ঠানিক নিয়মগুলো (1NF, 2NF, 3NF) আছে। আপাতত মোটা দাগের নিয়ম: একই তথ্য দুই জায়গায় লেখা থাকলে একদিন না একদিন দুটো আলাদা কথা বলবে।
ফ্ল্যাট টেবিল মানেই খারাপ নয়। মডেলের ট্রেনিং সেট বা ড্যাশবোর্ডের পেছনের টেবিল প্রায়ই ইচ্ছে করেই ফ্ল্যাট (ডিনরমালাইজড) রাখা হয়, কারণ মডেল বা চার্টের সেটাই দরকার। আসল কৌশল হলো নরমালাইজড টেবিলগুলো থেকে কোয়েরি দিয়ে সেটা বানানো, ফ্ল্যাট কপিটাকে মূল উৎস (source of truth) হিসেবে না রাখা।
সাধারণ ভুল
- SQLite-এ ফরেন কি চালু করতে ভুলে যাওয়া। সাধারণ একটা
sqlite3.connect()এগুলো যাচাই করে না, তাই অনাথ (orphan) সারি চুপচাপ ঢুকে পড়ে:import sqlite3 raw = sqlite3.connect("shop.db") # PRAGMA foreign_keys = ON নেই raw.execute("INSERT INTO orders VALUES (99, 42, '2026-07-01', 'pending')") print("orphan orders:", raw.execute("SELECT COUNT(*) FROM orders WHERE customer_id = 42").fetchone()[0]) raw.rollback() # যা ঢুকেছিল তা বাতিল raw.execute("PRAGMA foreign_keys = ON") # সমাধান: প্রতিটা নতুন কানেকশনে এটা চালান try: raw.execute("INSERT INTO orders VALUES (99, 42, '2026-07-01', 'pending')") except sqlite3.IntegrityError as error: print("rejected:", error) raw.close()orphan orders: 1 rejected: FOREIGN KEY constraint failed - নামকে কি বানানো। "Nadia Rahman" নামের দুজন কাস্টমার একজন হয়ে যান। প্রাইমারি কি হিসেবে একটা পূর্ণসংখ্যার id নিন, আর স্বাভাবিক মানগুলো (ইমেইল) রাখুন
UNIQUEকলামে, ঠিক যেমনcustomersকরে। - ধরে নেওয়া যে সম্পর্কটা ওয়ান-টু-ওয়ান। টেবিল মেলানোর আগে যাচাই করুন। একাধিক পেমেন্ট আছে এমন অর্ডার (
GROUP BYআরHAVINGশিখবেন অ্যাগ্রিগেট, GROUP BY ও HAVING পাতায়):SELECT order_id, COUNT(*) AS payments FROM payments GROUP BY order_id HAVING COUNT(*) > 1 ORDER BY order_id;+----------+----------+ | order_id | payments | +----------+----------+ | 6 | 2 | +----------+----------+ - একটা কলামে তালিকা রাখা, যেমন অর্ডারে
products = '1,4'। এটা ইনডেক্স করা যায় না, জয়েন করা যায় না, কেউ'1,,x'লিখে দিলে আটকানোও যায় না। জাংশন টেবিল ব্যবহার করুন: প্রতিটা অর্ডার-প্রোডাক্ট জোড়ার জন্য একটা সারি, যেমনorder_items-এ।
নিজে চেষ্টা করুন
- সহজ:
products-এর প্রাইমারি কি কোনটা, আর এর কোন কলামটা ফরেন কি, কোন দিকে নির্দেশ করে? একটাPRAGMAদিয়ে উত্তর মিলিয়ে নিন। - মাঝারি: অর্ডার ৪-এর
order_itemsসারিগুলো দেখান। তারপরproductsথেকে সেই প্রোডাক্টগুলোর নাম বের করুন। - কঠিন: একটা ওয়ান-টু-ওয়ান টেবিল
customer_profilesবানান (customer_id, যা একই সাথে PK আরcustomers-এর দিকে FK, সাথেpreferred_language)। কাস্টমার ১-এর জন্য একটা প্রোফাইল যোগ করুন, তারপর দেখান যে কাস্টমার ৪২-এর প্রোফাইল আর কাস্টমার ১-এর দ্বিতীয় প্রোফাইল — দুটোই বাতিল হয়।
উত্তর
১. PK হলো product_id; category_id হলো categories.category_id-এর দিকে FK:
PRAGMA foreign_key_list(products);২. অর্ডার ৪-এ আছে প্রোডাক্ট ২ আর ৮ (Hands-On Machine Learning আর Laptop Stand):
SELECT order_id, product_id, quantity, unit_price
FROM order_items
WHERE order_id = 4
ORDER BY product_id;
SELECT product_id, name
FROM products
WHERE product_id IN (2, 8)
ORDER BY product_id;৩. customer_id-কে একই সাথে প্রাইমারি কি আর ফরেন কি বানালে নিয়মটা দাঁড়ায় "প্রতি কাস্টমারের বড়জোর একটা প্রোফাইল, আর শুধু আসল কাস্টমারের":
CREATE TABLE customer_profiles (
customer_id INTEGER PRIMARY KEY,
preferred_language VARCHAR(20) NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers (customer_id)
);
INSERT INTO customer_profiles VALUES (1, 'bn');INSERT INTO customer_profiles VALUES (42, 'en');Error: FOREIGN KEY constraint failedINSERT INTO customer_profiles VALUES (1, 'en');Error: UNIQUE constraint failed: customer_profiles.customer_idসারসংক্ষেপ
- একটা টেবিলে থাকে এক ধরনের জিনিস; সারিগুলো সেই জিনিস, কলামগুলো তাদের তথ্য। সারির নিজস্ব কোনো ক্রম নেই।
- প্রাইমারি কি প্রতিটা সারিকে চেনায়: অনন্য, কখনো NULL নয়, আদর্শভাবে একটা অর্থহীন পূর্ণসংখ্যা। কম্পোজিট কি-তে একাধিক কলাম থাকে, যেমন
order_items-এ। - ফরেন কি-কে অন্য টেবিলের একটা প্রাইমারি কি-র সাথে মিলতেই হবে; অনাথ সারি, আর যে ডিলিট অনাথ সারি তৈরি করবে — দুটোই ডেটাবেস আটকে দেয়। SQLite-এ এটা চালু করুন
PRAGMA foreign_keys = ONদিয়ে। - ওয়ান-টু-মেনিতে "মেনি" দিকে থাকে FK; মেনি-টু-মেনিতে লাগে জাংশন টেবিল; ওয়ান-টু-ওয়ানে এমন FK, যা অনন্যও। ডেটা যাচাই করুন — অর্ডার থেকে পেমেন্ট আসলে ওয়ান-টু-মেনি।
- প্রতিটা তথ্য একবার রাখুন (নরমালাইজেশন), আর মডেল ও ড্যাশবোর্ডের ফ্ল্যাট টেবিল কোয়েরি দিয়ে সেখান থেকে বানান।
এরপর: সেটআপ: আপনার অনুশীলনের ডেটাবেস পাতায় shop.sql থেকে আপনার কম্পিউটারে shop.db বানাবেন, যাতে এই পাতাগুলোর প্রতিটা কোয়েরি নিজে চালাতে পারেন।