অধ্যায় 3 · ডেটা তৈরি ও বদলানো
INSERT, UPDATE, DELETE আর আপসার্ট, নিরাপদে
- পৃষ্ঠা 12 / 22
- 14 মিনিট পড়া
ডেটা পড়া নিরাপদ: ভুল SELECT বড়জোর ভুল সারি দেখায়। ডেটা বদলানো তেমন নয়। INSERT, UPDATE আর DELETE হলো DML (data manipulation language) স্টেটমেন্ট, আর প্রতিটা অ্যাপ এগুলো দিয়েই ডেটাবেসে লেখে: নতুন সাইন-আপ, দাম বদল, বাতিল অর্ডার। AI কাজে এগুলো দিয়েই আপনি মডেলের প্রতিটা প্রেডিকশন লগ করেন, মানুষ অ্যানোটেটরের দেওয়া লেবেল জমা রাখেন, আর ডেটা পরিষ্কারের সময় পাওয়া ভুল রেকর্ড ঠিক করেন।
এই পাতায় স্টেটমেন্টগুলো শিখবেন, আর তার মতোই জরুরি আরেকটা জিনিস: এগুলো নিশ্চিন্তে চালানোর একটা রুটিন। আগে যাচাই, তারপর ট্রানজ্যাকশনের ভেতরে বদল, মিলিয়ে দেখা, শেষে কমিট।
যা শিখবেন
- কলামের তালিকাসহ
INSERT, একসাথে অনেক সারি, আরINSERT … SELECT। UPDATE … SET … WHEREআরDELETE … WHERE, আর কনস্ট্রেইন্ট কীভাবে এদের পাহারা দেয়।- আপসার্ট (upsert):
ON CONFLICT(SQLite, PostgreSQL) আরON DUPLICATE KEY UPDATE(MySQL), সাথেRETURNING। - ডেটা বদলানোর নিরাপদ রুটিন: আগে
SELECT,BEGIN/ROLLBACK, সারির হিসাব, ব্যাকআপ। - Python থেকে প্যারামিটার,
executemanyআরcommitদিয়ে ডেটা লেখা।
CRUD: ডেটা নিয়ে চার রকম কাজ
| কাজ | SQL | ওয়েব API-তে | শপের উদাহরণ |
|---|---|---|---|
| Create (তৈরি) | INSERT | POST | নতুন কাস্টমারের সাইন-আপ |
| Read (পড়া) | SELECT | GET | একজন কাস্টমারের অর্ডার দেখানো |
| Update (বদল) | UPDATE | PUT / PATCH | প্রোডাক্ট রিস্টক করা |
| Delete (মোছা) | DELETE | DELETE | অ্যাকাউন্ট সরিয়ে ফেলা |
INSERT: সারি যোগ করা
সবসময় কলামের নাম লিখুন। তাহলে কেউ নতুন কলাম যোগ করলে বা কলামের ক্রম বদলালেও স্টেটমেন্টটা কাজ করবে, আর যে পড়বে সে বুঝবে কোন মান কোথায় যাচ্ছে। একাধিক সারি একটা স্টেটমেন্টেই দেওয়া যায়, কমা দিয়ে আলাদা করে:
INSERT INTO customers (customer_id, name, email, city, joined_on)
VALUES (9, 'Rina Begum', 'rina@example.com', 'Dhaka', '2026-06-20'),
(10, 'Arif Mollah', 'arif@example.com', NULL, '2026-06-21');
INSERT INTO categories (name) VALUES ('Software'); -- category_id নিজে থেকেই বসে
SELECT customer_id, name, city FROM customers WHERE customer_id >= 9 ORDER BY customer_id;
SELECT * FROM categories ORDER BY category_id;+-------------+-------------+-------+
| customer_id | name | city |
+-------------+-------------+-------+
| 9 | Rina Begum | Dhaka |
| 10 | Arif Mollah | NULL |
+-------------+-------------+-------+
+-------------+-------------+
| category_id | name |
+-------------+-------------+
| 1 | Books |
| 2 | Electronics |
| 3 | Courses |
| 4 | Accessories |
| 5 | Furniture |
| 6 | Software |
+-------------+-------------+যে কলাম বাদ দেন, সেখানে বসে তার DEFAULT, ডিফল্ট না থাকলে NULL। নতুন ক্যাটাগরি নিজে থেকেই ৬ নম্বর id পেয়েছে, কারণ category_id একটা INTEGER PRIMARY KEY। কলামের তালিকা না দিলে প্রতিটা কলামের মান দিতে হয়, টেবিলের ক্রম অনুযায়ী, আর না মিললে ব্যর্থ হয়:
INSERT INTO customers VALUES (11, 'Joy Saha', 'joy@example.com', '2026-06-22');Error: table customers has 5 columns but 4 values were suppliedRETURNING: যা ইনসার্ট করলেন তা ফেরত পাওয়া
আপনার প্রোগ্রামের সাধারণত ডেটাবেসের এইমাত্র বানানো id-টা দরকার হয়। RETURNING (SQLite 3.35+, PostgreSQL) নতুন সারিগুলোর কলাম ফেরত দেয়, যেন INSERT-টা একই সাথে একটা SELECT-ও:
INSERT INTO products (name, category_id, price, stock)
VALUES ('Data Science Handbook', 1, 1800, 20)
RETURNING product_id, name, price;+------------+-----------------------+-------+
| product_id | name | price |
+------------+-----------------------+-------+
| 12 | Data Science Handbook | 1800 |
+------------+-----------------------+-------+MySQL-এ RETURNING নেই; AUTO_INCREMENT টেবিলে ইনসার্টের পর SELECT LAST_INSERT_ID(); নতুন id দেয়। Python-এ sqlite3 আর MySQL ড্রাইভারে cursor.lastrowid এই id দেয়; PostgreSQL-এ RETURNING ব্যবহার করুন।
INSERT … SELECT: কোয়েরি থেকে সারি
VALUES-এর বদলে INSERT একটা SELECT-এর ফলাফলও নিতে পারে। আগের ডেটা থেকে এক স্টেটমেন্টে কাজের তালিকা, স্ন্যাপশট বা ট্রেনিং টেবিল এভাবেই বানানো হয়। এখানে, স্টকে ১০টার কম আছে এমন প্রোডাক্টের রিস্টক তালিকা:
CREATE TABLE restock_requests (
product_id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
order_qty INTEGER NOT NULL,
FOREIGN KEY (product_id) REFERENCES products (product_id)
);
INSERT INTO restock_requests (product_id, name, order_qty)
SELECT product_id, name, 20 - stock
FROM products
WHERE stock < 10;
SELECT * FROM restock_requests ORDER BY product_id;+------------+-----------------------------+-----------+
| product_id | name | order_qty |
+------------+-----------------------------+-----------+
| 5 | 27-inch Monitor | 15 |
| 9 | USB-C Hub | 20 |
| 10 | Noise-Cancelling Headphones | 12 |
+------------+-----------------------------+-----------+কোর্সগুলো (stock NULL) তালিকায় নেই: NULL < 10-এর ফল অজানা, সত্য নয়, তাই WHERE এগুলো বাদ দেয়; এখানে আমরা সেটাই চাই।
UPDATE: সারি বদলানো
UPDATE WHERE-এর সাথে মেলা প্রতিটা সারিতে নতুন মান বসায়। নতুন মানটা পুরোনো মান ব্যবহার করা এক্সপ্রেশনও হতে পারে:
UPDATE products
SET stock = stock + 20, price = 2100
WHERE product_id = 9;
SELECT product_id, name, price, stock FROM products WHERE product_id = 9;+------------+-----------+-------+-------+
| product_id | name | price | stock |
+------------+-----------+-------+-------+
| 9 | USB-C Hub | 2100 | 20 |
+------------+-----------+-------+-------+কনস্ট্রেইন্ট আপডেটেও খাটে। যে নিয়ম খারাপ INSERT আটকায়, সেটা একই খারাপ মান তৈরি করা UPDATE-ও আটকায়:
UPDATE orders SET status = 'returned' WHERE order_id = 14;Error: CHECK constraint failed: status IN ('pending', 'shipped', 'delivered', 'cancelled')DELETE: সারি মোছা
DELETE FROM … WHERE … মিলে যাওয়া সারিগুলো মুছে দেয়। ফরেন কি সম্পর্কিত ডেটা রক্ষা করে: যে কাস্টমারের এখনো অর্ডার আছে, তাকে মোছা যায় না।
DELETE FROM customers WHERE customer_id = 1;Error: FOREIGN KEY constraint failed-- PostgreSQL
DELETE FROM customers WHERE customer_id = 1;Error: update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"১০ নম্বর কাস্টমার কখনো অর্ডার করেননি, তাঁকে মোছা যায়:
DELETE FROM customers WHERE customer_id = 10;
SELECT COUNT(*) AS customers FROM customers;+-----------+
| customers |
+-----------+
| 9 |
+-----------+একটা স্টেটমেন্ট হয় পুরোটা হয়, নয়তো কিছুই না। একাধিক সারির INSERT-এ কোনো একটা সারি নিয়ম ভাঙলে কোনো সারিই জমা হয় না:
INSERT INTO orders (order_id, customer_id, order_date, status)
VALUES (15, 2, '2026-06-15', 'pending'),
(16, 3, '2026-06-16', 'returned');Error: CHECK constraint failed: status IN ('pending', 'shipped', 'delivered', 'cancelled')SELECT COUNT(*) AS orders FROM orders;+--------+
| orders |
+--------+
| 14 |
+--------+UPDATE আর DELETE-এর নিরাপদ রুটিন
সবচেয়ে পরিচিত বিপর্যয় হলো এমন UPDATE বা DELETE, যার WHERE নেই বা ভুল। একবার কমিট হয়ে গেলে আর ফেরানোর উপায় নেই। পেশাদাররা প্রতিবার একই ধাপগুলো মেনে চলেন:
- আগে SELECT।
WHERE-টা একটাSELECTহিসেবে লিখে দেখুন কোন সারিগুলো মেলে (অন্ততCOUNT(*))। BEGINদিয়ে ট্রানজ্যাকশন খুলুন, তারপর বদলটা চালান।- মিলিয়ে দেখুন কয়টা সারি বদলেছে (SQLite-এ
changes(), MySQL-এROW_COUNT(), PostgreSQL-এ "UPDATE 3" ধরনের মেসেজ, Python-এcursor.rowcount), আর ফলাফলটা দেখুন। - ঠিক থাকলে
COMMIT, না থাকলেROLLBACK। - বড় বদলের আগে ব্যাকআপ নিন।
দেখুন, ভুলে যাওয়া WHERE থেকে ট্রানজ্যাকশন কীভাবে বাঁচায়:
BEGIN;
UPDATE products SET stock = 0; -- ওহো: লিখতে চেয়েছিলাম WHERE product_id = 10
SELECT changes() AS rows_changed;
ROLLBACK; -- 12টা সারি তো পরিকল্পনায় ছিল না: বাতিল
SELECT product_id, stock FROM products WHERE product_id IN (1, 2, 3) ORDER BY product_id;+--------------+
| rows_changed |
+--------------+
| 12 |
+--------------+
+------------+-------+
| product_id | stock |
+------------+-------+
| 1 | 40 |
| 2 | 15 |
| 3 | 12 |
+------------+-------+ট্রানজ্যাকশনের ভেতরে ক্ষতিটা সত্যিই হয়েছিল, কিন্তু আর কেউ তা দেখতে পায়নি, আর ROLLBACK প্রতিটা সারি আগের জায়গায় ফিরিয়ে দিয়েছে। MySQL সেফ-আপডেট মোডে (safe-update mode) এমন স্টেটমেন্ট সরাসরি ফিরিয়েও দিতে পারে (MySQL Workbench এটা ডিফল্টভাবে চালু রাখে)। তখন UPDATE/DELETE চলে কেবল যদি WHERE-এ কোনো কি কলাম থাকে বা স্টেটমেন্টে LIMIT থাকে:
-- MySQL
SET SQL_SAFE_UPDATES = 1;
UPDATE products SET stock = 0;Error: ERROR 1175: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. ব্যাকআপের জন্য sqlite3 শেলে আছে .backup shop-backup.db, MySQL-এ mysqldump, PostgreSQL-এ pg_dump। Python থেকে SQLite চালু অবস্থাতেই এক কলে পুরো ডেটাবেস কপি করতে পারে:
import sqlite3
from sqlhelp import con
backup = sqlite3.connect("shop-backup.db")
con.backup(backup) # shop.db-এর পুরো, ঠিকঠাক একটা কপি
n = backup.execute("SELECT COUNT(*) FROM products").fetchone()[0]
print("products in backup:", n)
backup.close()products in backup: 12আপসার্ট: ইনসার্ট, আর আগে থেকে থাকলে আপডেট
ডেটা পাইপলাইন বারবার চলে। একজন সাপ্লায়ার প্রতিদিন সকালে স্টকের ফাইল পাঠায়; কিছু প্রোডাক্ট নতুন, বেশিরভাগ আগে থেকেই আছে। সাধারণ INSERT প্রথম পুরোনো কি-তেই ব্যর্থ হয়, আর Python-এ "আগে খুঁজে দেখি, তারপর ইনসার্ট" ধীর আর একসাথে একাধিক প্রোগ্রাম চললে ভুল হতে পারে। আপসার্ট কাজটা এক স্টেটমেন্টেই সারে: সারিটা ইনসার্ট হবে, আর তার কি আগে থেকে থাকলে সেই পুরোনো সারিটাই আপডেট হবে। দুবার চালালেও ফল একই থাকে, আর এটাই পাইপলাইনকে আইডেমপোটেন্ট (idempotent) করে।
INSERT INTO products (product_id, name, category_id, price, stock)
VALUES (9, 'USB-C Hub', 4, 2200, 30),
(13, 'Webcam', 2, 3500, 10)
ON CONFLICT (product_id) DO UPDATE SET stock = excluded.stock
RETURNING product_id, name, price, stock;+------------+-----------+-------+-------+
| product_id | name | price | stock |
+------------+-----------+-------+-------+
| 9 | USB-C Hub | 2100 | 30 |
| 13 | Webcam | 3500 | 10 |
+------------+-----------+-------+-------+৯ নম্বর প্রোডাক্ট আগেই ছিল, তাই শুধু তার stock বদলেছে (দাম ২১০০-ই আছে); ১৩ নম্বর নতুন, তাই ইনসার্ট হয়েছে। excluded মানে "যে সারিটা আপনি ইনসার্ট করতে চেয়েছিলেন"। আগে থেকে থাকা সারি বাদ দিয়ে যেতে চাইলে লিখুন ON CONFLICT DO NOTHING। PostgreSQL-এ একই সিনট্যাক্স; MySQL-এর নিজস্ব। (সার্ভারগুলো শপের শুরুর ডেটা থেকে চলে, তাই সেখানে হাবের দাম এখনো ২২০০।)
-- PostgreSQL
INSERT INTO products (product_id, name, category_id, price, stock)
VALUES (9, 'USB-C Hub', 4, 2200, 30),
(13, 'Webcam', 2, 3500, 10)
ON CONFLICT (product_id) DO UPDATE SET stock = EXCLUDED.stock
RETURNING product_id, name, price, stock;+------------+-----------+-------+-------+
| product_id | name | price | stock |
+------------+-----------+-------+-------+
| 9 | USB-C Hub | 2200 | 30 |
| 13 | Webcam | 3500 | 10 |
+------------+-----------+-------+-------+-- MySQL (8.0.19+: AS দিয়ে নতুন সারির নাম দিন)
INSERT INTO products (product_id, name, category_id, price, stock)
VALUES (9, 'USB-C Hub', 4, 2200, 30),
(13, 'Webcam', 2, 3500, 10) AS new
ON DUPLICATE KEY UPDATE stock = new.stock;
SELECT product_id, name, price, stock FROM products WHERE product_id IN (9, 13) ORDER BY product_id;+------------+-----------+-------+-------+
| product_id | name | price | stock |
+------------+-----------+-------+-------+
| 9 | USB-C Hub | 2200 | 30 |
| 13 | Webcam | 3500 | 10 |
+------------+-----------+-------+-------+পুরোনো MySQL কোডে লেখা থাকে stock = VALUES(stock); এটা এখনো চলে, তবে বাতিলের পথে (deprecated)। আর MySQL-এর এই রূপটা যেকোনো ইউনিক কি-তে কনফ্লিক্ট হলেই কাজ করে; কোন কি ধরবে, তা আপনি বেছে দিতে পারেন না।
REPLACE-এর ফাঁদ
SQLite-এর INSERT OR REPLACE (আর MySQL-এর REPLACE) দেখতে আপসার্টের মতো, কিন্তু আসলে নয়: কনফ্লিক্ট হলে এরা পুরোনো সারিটা মুছে নতুনটা ইনসার্ট করে। যে কলামের মান দেননি, সব হারিয়ে যায়:
INSERT OR REPLACE INTO products (product_id, name, price) VALUES (4, 'Wireless Mouse', 950);
SELECT product_id, name, category_id, price, stock FROM products WHERE product_id = 4;+------------+----------------+-------------+-------+-------+
| product_id | name | category_id | price | stock |
+------------+----------------+-------------+-------+-------+
| 4 | Wireless Mouse | NULL | 950 | NULL |
+------------+----------------+-------------+-------+-------+মাউসটা তার ক্যাটাগরি আর স্টকের হিসাব হারিয়েছে। ON CONFLICT … DO UPDATE ব্যবহার করুন, আর ঠিক যে কলামগুলো বদলাবেন সেগুলোর নাম লিখুন। এগোনোর আগে শপটা আগের অবস্থায় ফিরিয়ে নিই:
from sqlhelp import con, reset
con.execute("DROP TABLE IF EXISTS restock_requests") # আমাদের বানানো টেবিল, products-কে রেফার করে
reset() # শপের টেবিলগুলো শুরুর সারিতে ফিরে যায়
print(con.execute("SELECT category_id, stock FROM products WHERE product_id = 4").fetchone())(2, 60)Python থেকে ডেটা লেখা
আসল ইনসার্ট আসে প্রোগ্রাম থেকে: ইমপোর্ট স্ক্রিপ্ট, API হ্যান্ডলার, নিজের প্রেডিকশন লগ করা মডেল। ? প্লেসহোল্ডার ব্যবহার করুন আর মানগুলো আলাদা করে পাঠান; কখনো মান SQL স্ট্রিংয়ের ভেতরে জুড়ে দেবেন না (SQL injection এভাবেই হয়; অ্যাডভান্সড টিউটোরিয়ালে হাতেকলমে দেখানো আছে)। executemany পুরো একটা লিস্টের জন্য একই স্টেটমেন্ট চালায়, আর commit() না করা পর্যন্ত কিছুই সেভ হয় না:
from sqlhelp import con, show
new_customers = [
(9, "Rina Begum", "rina@example.com", "Dhaka", "2026-06-20"),
(10, "Arif Mollah", "arif@example.com", "Barishal", "2026-06-21"),
]
con.executemany(
"INSERT INTO customers (customer_id, name, email, city, joined_on) VALUES (?, ?, ?, ?, ?)",
new_customers,
)
cur = con.execute("UPDATE products SET price = price + ? WHERE category_id = ?", (100, 4))
print("rows updated:", cur.rowcount)
con.commit() # বদলগুলো স্থায়ী করুন
show("SELECT customer_id, name, city FROM customers WHERE customer_id >= 9 ORDER BY customer_id")rows updated: 2
+-------------+-------------+----------+
| customer_id | name | city |
+-------------+-------------+----------+
| 9 | Rina Begum | Dhaka |
| 10 | Arif Mollah | Barishal |
+-------------+-------------+----------+মাঝপথে কিছু ব্যর্থ হলে commit()-এর বদলে con.rollback() ডাকুন, আধা-করা কাজ মুছে যাবে। commit() ভুলে যাওয়া নতুনদের সবচেয়ে পরিচিত বাগ: প্রোগ্রাম "চলে", কিন্তু বন্ধ হলেই সারিগুলো উধাও, আর এর মধ্যে খোলা ট্রানজ্যাকশন অন্য প্রোগ্রামকে ফাইলটা থেকে আটকে রাখতে পারে।
বাস্তব সিস্টেমে কোথায় দেখবেন
- প্রেডিকশন লগ: মডেলের প্রতিটা কল হলো ইনপুট, আউটপুট, মডেল ভার্সন আর লেটেন্সির একটা
INSERT; পরে এগুলো কোয়েরি করে ড্রিফট আর খরচ নজরে রাখা হয়। - এমবেডিং আর ফিচার ক্যাশ: ডকুমেন্ট id বা কনটেন্টের হ্যাশ দিয়ে চিহ্নিত, আর আপসার্ট দিয়ে লেখা; ফলে একটা ডকুমেন্ট আবার প্রসেস করলে তার সারি আপডেট হয়, ডুপ্লিকেট হয় না।
- ডেটা ঠিক করা: ভুল লেবেলের সারি সংশোধনে এককালীন একটা
UPDATE, যা আগে-SELECT আর ট্রানজ্যাকশনের রুটিনে চালানো হয় আর একজন সহকর্মী রিভিউ করেন।
সাধারণ ভুল
- WHERE ছাড়া UPDATE বা DELETE। সব সারি বদলে যায়। সমাধান: আগে
SELECTলিখুন, বদলটাBEGIN…COMMIT-এর ভেতরে রাখুন, সারির সংখ্যা মিলিয়ে নিন; MySQL-এSQL_SAFE_UPDATESব্যবহার করুন। - কলামের তালিকা ছাড়া INSERT। টেবিল বদলালে এটা ভাঙে, বা আরও খারাপ, চুপচাপ মান অদলবদল করে ফেলে। সমাধান:
INSERT INTO customers (customer_id, name, email, city, joined_on) VALUES (…)। - REPLACE-কে আপসার্ট হিসেবে ব্যবহার। এটা মুছে আবার ইনসার্ট করে, না দেওয়া কলামগুলো মুছে দেয় (আর MySQL-এ ডিলিট ক্যাসকেডও চালু করে দিতে পারে)। সমাধান:
ON CONFLICT (key) DO UPDATE SET col = excluded.colবা MySQL-এরON DUPLICATE KEY UPDATE। - Python-এ
commit()ভুলে যাওয়া। বদল হারিয়ে যায় আর ডেটাবেস লক হয়ে থাকে। সমাধান: লেখার পরcon.commit(), অথবাwith con:, যা সফল হলে কমিট আর এরর হলে রোলব্যাক করে। - f-string দিয়ে SQL বানানো।
O'Brien-এর মতো একটা নাম কোয়েরি ভেঙে দেয়, আর কেউ ইচ্ছে করে দিলে কোয়েরিটাই বদলে ফেলে। সমাধান: প্লেসহোল্ডার,con.execute("… WHERE email = ?", (email,))।
নিজে চেষ্টা করুন
প্রতিটা অনুশীলন আগেরটার ওপর নির্ভর করে; তাই ক্রম অনুযায়ী চালান।
- সহজ (Create): ৬ নম্বর id দিয়ে
'Software'ক্যাটাগরি যোগ করুন, আর তাতে'PyCharm Licence'নামে একটা প্রোডাক্ট, দাম ৯০০০, stock NULL। নতুন প্রোডাক্টটা দেখান। - মাঝারি (Update): শপ সব Books-এর দাম ১০% বাড়াচ্ছে। আগে গুনে দেখুন কয়টা সারি বদলাবেন, তারপর ট্রানজ্যাকশনের ভেতরে আপডেট করুন,
changes()মিলিয়ে কমিট করুন, আর বইগুলো দেখান। - কঠিন (Upsert + Delete): একটা স্টক ফিড বলছে, ৩ নম্বর প্রোডাক্টের এখন স্টক ৭টা, আর নতুন প্রোডাক্ট ১৩,
'Graphics Tablet'(ক্যাটাগরি ২, দাম ৬৫০০), আছে ৪টা। এমন একটা আপসার্ট দিয়ে এটা প্রয়োগ করুন, যা পুরোনো প্রোডাক্টের দামও আপডেট করে। তারপর ১ নম্বর অনুশীলনে বানানো প্রোডাক্টটা নিরাপদে মুছুন।
উত্তর
-- সহজ
INSERT INTO categories (category_id, name) VALUES (6, 'Software');
INSERT INTO products (product_id, name, category_id, price, stock)
VALUES (12, 'PyCharm Licence', 6, 9000, NULL);
SELECT * FROM products WHERE product_id = 12;+------------+-----------------+-------------+-------+-------+
| product_id | name | category_id | price | stock |
+------------+-----------------+-------------+-------+-------+
| 12 | PyCharm Licence | 6 | 9000 | NULL |
+------------+-----------------+-------------+-------+-------+-- মাঝারি
SELECT COUNT(*) AS books FROM products WHERE category_id = 1; -- আশা করছি 2
BEGIN;
UPDATE products SET price = price * 110 / 100 WHERE category_id = 1;
SELECT changes() AS rows_changed; -- 2: মিলেছে, তাই কমিট
COMMIT;
SELECT product_id, name, price FROM products WHERE category_id = 1 ORDER BY product_id;+-------+
| books |
+-------+
| 2 |
+-------+
+--------------+
| rows_changed |
+--------------+
| 2 |
+--------------+
+------------+---------------------------+-------+
| product_id | name | price |
+------------+---------------------------+-------+
| 1 | Python Crash Course | 1320 |
| 2 | Hands-On Machine Learning | 2750 |
+------------+---------------------------+-------+-- কঠিন
INSERT INTO products (product_id, name, category_id, price, stock)
VALUES (3, 'Mechanical Keyboard', 2, 4500, 7),
(13, 'Graphics Tablet', 2, 6500, 4)
ON CONFLICT (product_id) DO UPDATE SET stock = excluded.stock, price = excluded.price;
SELECT COUNT(*) AS to_delete FROM products WHERE product_id = 12; -- আগে যাচাই: 1টা সারি
BEGIN;
DELETE FROM products WHERE product_id = 12;
SELECT changes() AS rows_deleted;
COMMIT;
SELECT product_id, name, price, stock FROM products WHERE product_id IN (3, 12, 13) ORDER BY product_id;+-----------+
| to_delete |
+-----------+
| 1 |
+-----------+
+--------------+
| rows_deleted |
+--------------+
| 1 |
+--------------+
+------------+---------------------+-------+-------+
| product_id | name | price | stock |
+------------+---------------------+-------+-------+
| 3 | Mechanical Keyboard | 4500 | 7 |
| 13 | Graphics Tablet | 6500 | 4 |
+------------+---------------------+-------+-------+সারসংক্ষেপ
INSERT INTO t (cols) VALUES (…), (…)সারি যোগ করে; সবসময় কলামের নাম লিখুন।INSERT … SELECTকোয়েরির ফলাফল টেবিলে কপি করে।UPDATE t SET … WHERE …আরDELETE FROM t WHERE …মিলে যাওয়া সারি বদলায় ও মোছে; কনস্ট্রেইন্ট আর ফরেন কি দুটোকেই পাহারা দেয়, আর একটা স্টেটমেন্ট হয় পুরোটা হয়, নয়তো কিছুই না।- আপসার্ট আবার চালানোকে নিরাপদ করে:
ON CONFLICT … DO UPDATE / DO NOTHING(SQLite, PostgreSQL),ON DUPLICATE KEY UPDATE(MySQL)।REPLACEআপসার্ট নয়। RETURNING(SQLite 3.35+, PostgreSQL) বদলানো সারিগুলো ফেরত দেয়; MySQL ব্যবহার করেLAST_INSERT_ID()।- নিরাপদ রুটিন: আগে SELECT,
BEGIN, বদল, সারির সংখ্যা যাচাই,COMMITবাROLLBACK, আর বড় বদলের আগে ব্যাকআপ। Python-এ: প্লেসহোল্ডার আরcommit()।
এরপর: টেক্সট, সংখ্যা ও তারিখের ফাংশন, CAST আর COALESCE। ডেটা ঢোকানো শিখে গেছেন; এবার শিখবেন সেই ফাংশনগুলো, যা কোয়েরির ভেতরেই ডেটা পরিষ্কার করে আর নতুন আকার দেয়: টেক্সট ছাঁটা ও ভাঙা, রাউন্ড করা, তারিখের হিসাব, টাইপ বদল, আর NULL ও শূন্য দিয়ে ভাগ নিরাপদে সামলানো।