অধ্যায় 2 · ডেটা পড়া
এরর পড়া আর কোয়েরি ডিবাগ করা
- পৃষ্ঠা 9 / 22
- 15 মিনিট পড়া
যিনিই SQL লেখেন, নতুন হোন বা অভিজ্ঞ, সারা দিন এরর মেসেজ দেখেন। পার্থক্য হলো, অভিজ্ঞ মানুষ মেসেজটা পড়েন, কয়েক সেকেন্ডে ঠিক জায়গাটা খুঁজে বের করেন, আর এগিয়ে যান। ডেটাবেসের এরর ছোট কিন্তু নিখুঁত: কোন শব্দে এসে পার্সিং ভেঙেছে, কোন টেবিল বা কলাম নেই, কোন সারি কোন নিয়ম ভেঙেছে, সব নাম ধরে বলে।
কঠিন বাগগুলো হলো সেগুলো, যাদের কোনো মেসেজই নেই: কোয়েরি চলে, সুন্দর একটা টেবিল ফেরত দেয়, আর সংখ্যাগুলো ভুল। এই পাতায় দুটোই আছে। এখন LLM SQL লেখে বলে এটা আরও জরুরি: একটা text-to-SQL সিস্টেম ততটাই ভালো, যতটা সে এরর ধরতে, পড়তে আর কোয়েরি সারাতে পারে, আর দেখতে ঠিক লাগা একটা ফলাফল যাচাই করতে পারে।
যা শিখবেন
- সাধারণ এররগুলো পড়া: সিনট্যাক্স, টেবিল বা কলাম নেই, অস্পষ্ট (ambiguous) কলাম, কনস্ট্রেইন্ট ভাঙা আর টাইপের অমিল; SQLite, PostgreSQL আর MySQL-এ।
- নাম ভুল হলে টেবিলের আসল কলামগুলো দেখে নেওয়া।
- Python-এ ডেটাবেসের এরর ধরা আর মেসেজটা কাজে লাগানো।
- এরর ছাড়াই ভুল ফলাফল দেওয়া লজিক্যাল বাগ চেনা।
- একটা ডিবাগিং রুটিন মেনে চলা: কোয়েরি ছোট করা, টুকরোগুলো আলাদা চালানো, সারি গোনা, আর UPDATE বা DELETE-এর আগে SELECT।
সিনট্যাক্স এরর: যে শব্দের কথা বলে, সেটা পড়ুন
সিনট্যাক্স এরর মানে ডেটাবেস স্টেটমেন্টটা বুঝতেই পারেনি। মেসেজটা সেই জায়গার নাম বলে, যেখানে এসে সে গুলিয়ে ফেলেছে:
SELECT name, FROM products;Error: near "FROM": syntax errornear "FROM" মানে এই নয় যে FROM ভুল। মানে হলো, পার্সার ওই পর্যন্ত ঠিকঠাক এসেছে, তারপর এমন একটা শব্দ পেয়েছে যা ওখানে আসতে পারে না। আসল ভুলটা সাধারণত নাম বলা শব্দের ঠিক আগে: এখানে name-এর পরের কমা আরেকটা কলামের প্রতিশ্রুতি দেয়, আর FROM কোনো কলাম নয়। একই এরর, তিন রকমে:
-- PostgreSQL
SELECT name, FROM products;Error: syntax error at or near "FROM"-- MySQL
SELECT name, FROM products;Error: ERROR 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM products' at line 2MySQL গোলমালের জায়গা থেকে বাকি টেক্সটটা উদ্ধৃত করে (near 'FROM products') আর একটা লাইন নম্বর দেয়; সেই গোনায় -- MySQL কমেন্টটাও ধরা হয়। ক্লজের ক্রম ভুল হলেও একই ধরনের এরর আসে, কারণ ক্রমটা নির্দিষ্ট: SELECT … FROM … WHERE … ORDER BY … LIMIT।
SELECT name FROM products ORDER BY price DESC LIMIT 3 WHERE stock > 0;Error: near "WHERE": syntax errorআরও কিছু চেনা সন্দেহভাজন: শেষের উদ্ধৃতিচিহ্ন বাদ পড়া (তার পরের সবকিছু "টেক্সট" হয়ে যায়), শেষের বন্ধনী বাদ পড়া, কোনো কিওয়ার্ডকে নাম হিসেবে ব্যবহার, আর FROM-এর আগে বা শেষ কলামের পরে একটা বাড়তি কমা।
টেবিল নেই, কলাম নেই
স্টেটমেন্টটা বৈধ SQL, কিন্তু এমন কিছুর নাম নিচ্ছে যার অস্তিত্ব নেই। প্রায় সবসময়ই টাইপো, একবচন-বহুবচনের ভুল, বা এমন কলাম যা আসলে অন্য টেবিলে থাকে:
SELECT * FROM product;Error: no such table: productSELECT name, status FROM products;Error: no such column: statusstatus আছে, তবে orders-এ। আন্দাজ করবেন না: কলামগুলো দেখে নিন। SQLite-এ PRAGMA table_info কলামগুলোর লিস্ট দেয়, ঘোষিত টাইপসহ:
PRAGMA table_info(products);+-----+-------------+--------------+---------+------------+----+
| cid | name | type | notnull | dflt_value | pk |
+-----+-------------+--------------+---------+------------+----+
| 0 | product_id | INTEGER | 0 | NULL | 1 |
| 1 | name | VARCHAR(100) | 1 | NULL | 0 |
| 2 | category_id | INTEGER | 0 | NULL | 0 |
| 3 | price | INTEGER | 1 | NULL | 0 |
| 4 | stock | INTEGER | 0 | NULL | 0 |
+-----+-------------+--------------+---------+------------+----+অন্য জায়গায় এর সমতুল্য হলো PostgreSQL-এর psql শেলে \d products, MySQL-এ DESCRIBE products;, আর sqlite3 শেলে .schema products। DB Browser for SQLite একই জিনিস দেখায় তার Database Structure ট্যাবে। PostgreSQL এই এররগুলো লেখে relation "product" does not exist (relation মানে টেবিল বা ভিউ) আর column "prise" does not exist হিসেবে; MySQL লেখে ERROR 1146: Table … doesn't exist আর ERROR 1054: Unknown column 'prise' in 'field list'।
ডাবল কোট: SQLite-এ চলে, অন্য জায়গায় এরর দেয়
স্ট্যান্ডার্ড SQL-এ 'সিঙ্গেল কোট' মানে টেক্সট, আর "ডাবল কোট" মানে কলাম বা টেবিলের নাম। SQLite "Gift Card" লিখলেও মেনে নেয়: ওই নামে কোনো কলাম না থাকলে সে এটাকে টেক্সট ধরে নেয়। PostgreSQL তা করে না:
-- PostgreSQL
SELECT name FROM products WHERE name = "Gift Card";Error: column "Gift Card" does not existএরর যখন বলে কোনো কলাম নেই, আর সেই "কলাম" স্পষ্টতই একটা মান, তখন উদ্ধৃতিচিহ্নগুলো দেখুন।
অস্পষ্ট কলাম (ambiguous column)
কোয়েরি যখন এমন দুটো টেবিল থেকে পড়ে যাদের একই নামের কলাম আছে, ডেটাবেস জানতে পারে না আপনি কোনটা বোঝাচ্ছেন। এই টিউটোরিয়ালে পরে টেবিল জয়েন করবেন; তখন যে এররটা পাবেন, সেটা এই:
SELECT order_id, product_id, status
FROM orders
JOIN order_items ON orders.order_id = order_items.order_id;Error: ambiguous column name: order_idদুই টেবিলেই order_id আছে। সমাধান হলো কোনটা, তা বলে দেওয়া: SELECT orders.order_id, …। PostgreSQL বলে column reference "order_id" is ambiguous; MySQL বলে Column 'order_id' in field list is ambiguous। আরও আছে জয়েন: INNER JOIN আর টেবিল অ্যালিয়াস পাতায়।
কনস্ট্রেইন্ট ভাঙা
কনস্ট্রেইন্ট হলো এমন একটা নিয়ম, যা টেবিল প্রতিটা সারির ওপর খাটায় (এগুলো বানাবেন CREATE, ALTER, DROP আর কনস্ট্রেইন্ট পাতায়)। কোনো INSERT বা UPDATE একটা নিয়ম ভাঙলে ডেটাবেস পুরো স্টেটমেন্টটাই ফিরিয়ে দেয়। নিচের Python লুপ পাঁচটা ভুল INSERT চালানোর চেষ্টা করে আর প্রতিটার এরর প্রিন্ট করে:
import sqlite3
from sqlhelp import con
attempts = {
"duplicate email": "INSERT INTO customers VALUES (9, 'Test User', 'nadia@example.com', NULL, '2026-07-01')",
"missing name": "INSERT INTO customers VALUES (9, NULL, 'test@example.com', NULL, '2026-07-01')",
"free product": "INSERT INTO products VALUES (12, 'Free Sticker', 4, 0, 100)",
"unknown customer": "INSERT INTO orders VALUES (15, 99, '2026-07-01', 'pending')",
"unknown status": "INSERT INTO orders VALUES (15, 1, '2026-07-01', 'lost')",
}
for label, sql in attempts.items():
try:
con.execute(sql)
except sqlite3.IntegrityError as error:
print(f"{label:17} {error}")
con.rollback()duplicate email UNIQUE constraint failed: customers.email
missing name NOT NULL constraint failed: customers.name
free product CHECK constraint failed: price > 0
unknown customer FOREIGN KEY constraint failed
unknown status CHECK constraint failed: status IN ('pending', 'shipped', 'delivered', 'cancelled')| মেসেজ | নিয়মটা | সাধারণ কারণ |
|---|---|---|
UNIQUE constraint failed | দুটো সারির এই মান এক হতে পারবে না | একই রেকর্ড দুবার ইমপোর্ট; এমন একটা রিট্রাই, যা আগেই সফল হয়ে গিয়েছিল |
NOT NULL constraint failed | এই কলামে মান থাকতেই হবে | ইনপুটে একটা ফিল্ড নেই; INSERT-এ একটা কলাম বাদ পড়েছে |
CHECK constraint failed | প্রতিটা সারিকে একটা শর্ত মানতে হবে | খারাপ মান: শূন্য দাম, অচেনা স্ট্যাটাস, ঋণাত্মক পরিমাণ |
FOREIGN KEY constraint failed | যে সারির উল্লেখ করা হচ্ছে, তা থাকতে হবে | প্যারেন্টের আগে চাইল্ড ইনসার্ট; ভুল id |
UNIQUE আর NOT NULL-এর বেলায় SQLite টেবিল আর কলামের নাম বলে, CHECK-এর বেলায় নিয়মটা প্রিন্ট করে, কিন্তু কোন ফরেন কি ভেঙেছে তা কিছুই বলে না। PostgreSQL বেশি সাহায্য করে:
-- PostgreSQL
INSERT INTO orders VALUES (15, 99, '2026-07-01', 'pending');Error: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"কনস্ট্রেইন্ট এরর আসলে সুখবর: ডেটাবেস খারাপ ডেটাকে দরজাতেই আটকে দিয়েছে। ডেটা পাইপলাইনে এগুলো ধরুন, দোষী সারিটা লগ করুন, আর বাকিগুলো নিয়ে এগিয়ে যান; একটা খারাপ সারির জন্য পুরো লোড যেন ভেঙে না পড়ে।
উল্টো দিকের একটা চমক, যেখানে এরর আসার কথা কিন্তু আসে না: কলামের সংজ্ঞার ভেতরে লেখা
REFERENCES(customer_id INTEGER REFERENCES customers (customer_id)) MySQL 8.0 চুপচাপ উপেক্ষা করে। এভাবে লেখা টেবিলে MySQL-এ কোনো ফরেন কি থাকে না, আর অস্তিত্বহীন কাস্টমার ৯৯-এর অর্ডারও টুঁ শব্দ না করে জমা হয়ে যায়। এ কারণেইshop.sqlপ্রতিটা ফরেন কি আলাদাFOREIGN KEY (customer_id) REFERENCES customers (customer_id)ক্লজ হিসেবে লেখে, যা তিনটা ডেটাবেসই মেনে চলে। বিস্তারিত CREATE, ALTER, DROP আর কনস্ট্রেইন্ট পাতায়।
টাইপের অমিল
সংখ্যার কলামে টেক্সট রাখলে কী হবে, তা অনেকটাই ডেটাবেসের ওপর নির্ভর করে। SQLite সবচেয়ে উদার, কখনো কখনো একটু বেশিই:
INSERT INTO products VALUES (12, 'Sticker', 4, 'cheap', 100);
SELECT product_id, name, price, typeof(price) AS stored_as
FROM products
WHERE product_id = 12;
DELETE FROM products WHERE product_id = 12;+------------+---------+-------+-----------+
| product_id | name | price | stored_as |
+------------+---------+-------+-----------+
| 12 | Sticker | cheap | text |
+------------+---------+-------+-----------+কোনো এরর নেই। SQLite একটা INTEGER কলামে cheap শব্দটা রেখে দিয়েছে, এমনকি CHECK (price > 0)-ও পাস করেছে, কারণ SQLite-এ যেকোনো টেক্সট যেকোনো সংখ্যার চেয়ে বড় ধরা হয়। (SQLite-এর টাইপের নিয়ম আর এই ফুটো বন্ধ করা STRICT টেবিলের কথা পরের পাতায়।) সার্ভারগুলো ফিরিয়ে দেয়:
-- PostgreSQL
INSERT INTO products VALUES (12, 'Sticker', 4, 'cheap', 100);Error: invalid input syntax for type integer: "cheap"-- MySQL
INSERT INTO products VALUES (12, 'Sticker', 4, 'cheap', 100);Error: ERROR 1366: Incorrect integer value: 'cheap' for column 'price' at row 1ভিন্ন টাইপের তুলনাতেও PostgreSQL কড়া: WHERE order_date > 5 ব্যর্থ হয় operator does not exist: date > integer দিয়ে। MySQL ইনসার্টটা ফিরিয়ে দেয় শুধু এ কারণে যে সার্ভার strict মোডে চলছে (STRICT_TRANS_TABLES, 5.7 থেকে ডিফল্ট); সেটা না থাকলে MySQL 'cheap'-কে 0 বানিয়ে নিত, শুধু একটা ওয়ার্নিং দিয়ে, যা কেউ পড়ে না। এখানে অবশ্য তারপর CHECK সেই 0-কে আটকাত, কিন্তু এমন চেক ছাড়া কলামে 0 চুপচাপ জমা হয়ে যেত।
Python-এ এরর ধরা
Python থেকে sqlite3-এর প্রতিটা এরর sqlite3.Error-এর সাবক্লাস। সবচেয়ে বেশি দেখবেন দুটো: OperationalError (সিনট্যাক্স, টেবিল বা কলাম নেই) আর IntegrityError (কনস্ট্রেইন্ট)। মেসেজ স্ট্রিংটা হুবহু সেই টেক্সট, যা এতক্ষণ পড়ছিলেন; তাই এটা লগ করতে, বা যে LLM কোয়েরিটা লিখেছে তাকে ফেরত দিতে কাজে লাগে:
import sqlite3
from sqlhelp import con
def try_query(sql):
"""কোয়েরি চালায়; সারিগুলো ফেরত দেয়, নয়তো এররটা টেক্সট হিসেবে।"""
try:
return con.execute(sql).fetchall()
except sqlite3.Error as error:
return f"{type(error).__name__}: {error}"
# "৫০০০-এর বেশি দামের প্রোডাক্ট কয়টা?" প্রশ্নে একটা মডেল যা লিখতে পারে
print(try_query("SELECT COUNT(*) FROM product WHERE price > 5000"))
print(try_query("SELECT COUNT(*) FROM products WHERE price > 5000"))OperationalError: no such table: product
[(3,)]একটা text-to-SQL এজেন্ট লুপে ঠিক এটাই করে: কোয়েরি চালায়, ব্যর্থ হলে এররের টেক্সট আর টেবিলের লিস্ট মডেলকে ফেরত পাঠায়, যাতে মডেল নামটা ঠিক করতে পারে। এরর মেসেজ মানুষের জন্য লেখা, আর LLM-ও এগুলো ভালোই পড়ে।
যে বাগ কোনো এরর দেয় না
ডেটাবেস শুধু দেখে কোয়েরিটা বৈধ কিনা, আপনার প্রশ্নের উত্তর দিচ্ছে কিনা তা নয়। এগুলো দিব্যি চলে আর ভুল ফলাফল দেয়:
SELECT name price -- কমা নেই: "price" হয়ে গেছে name-এর অ্যালিয়াস
FROM products
ORDER BY product_id
LIMIT 3;+---------------------------+
| price |
+---------------------------+
| Python Crash Course |
| Hands-On Machine Learning |
| Mechanical Keyboard |
+---------------------------+SELECT 7 / 2 AS whole_numbers, -- পূর্ণসংখ্যার ভাগে .5 হারিয়ে যায়
7 / 2.0 AS one_decimal;+---------------+-------------+
| whole_numbers | one_decimal |
+---------------+-------------+
| 3 | 3.5 |
+---------------+-------------+পূর্ণসংখ্যার ভাগ ডায়ালেক্টের ফাঁদও: SQLite আর PostgreSQL দেয় 3, MySQL-এর / দেয় 3.5000। সবচেয়ে সাধারণ নীরব বাগগুলো, যাদের বেশিরভাগ গত তিন পাতায় দেখেছেন:
| বাগ | লক্ষণ | সমাধান |
|---|---|---|
| কলামের মাঝে কমা নেই | ভুল নামে ভুল ডেটার একটা কলাম | ফলাফলের হেডার সারিটা পড়ুন |
a OR b AND c | বাড়তি সারি | বন্ধনী |
= NULL, বা NULL-ওয়ালা কলামে <> | সারি হারিয়ে যায় | IS NULL, OR col IS NULL |
লিস্টে NULL-সহ NOT IN | খালি ফলাফল | NULL ছেঁকে ফেলুন, বা NOT EXISTS |
| পূর্ণসংখ্যার ভাগ (SQLite, PostgreSQL) | হার আর গড়ের দশমিক অংশ কেটে যায়, প্রায়ই ফল দাঁড়ায় ০ | আগে 1.0 দিয়ে গুণ করুন |
| ORDER BY নেই, বা টাই-ব্রেকার নেই | প্রতিবার আলাদা ক্রম | পুরো ক্রম ঠিক করে দেয় এমন ORDER BY |
| এমন জয়েন, যা একাধিক সারির সাথে মেলে | মোট বেশি আসে | দেখুন অনেক টেবিল জয়েন, আর ডুপ্লিকেট সারির ফাঁদ |
একটা ডিবাগিং রুটিন
কোয়েরি এরর দিলে, বা এমন কিছু ফেরত দিলে যা ঠিক লাগছে না, এলোমেলো এডিট না করে ধাপে ধাপে এগোন।
- পুরো মেসেজটা পড়ুন। যে শব্দ বা নামের দিকে ইঙ্গিত করছে সেটা খুঁজুন, আর তার ঠিক আগে দেখুন।
- কোয়েরি ছোট করুন। চলা পর্যন্ত লাইন কমেন্ট করে দিন, তারপর একটা একটা করে ফেরত আনুন। যে লাইন কোয়েরি ভাঙে, বা ফলাফল অপ্রত্যাশিতভাবে বদলায়, সেটাই আপনার বাগ।
- টুকরোগুলো চালান আর সারি গুনুন। প্রতিটা ধাপ কয়টা সারি রাখে দেখুন। অবাক করার মতো বড় বা ছোট সংখ্যা ভুল ধাপটার দিকে আঙুল তোলে।
- আসল মানগুলো দেখুন।
SELECT DISTINCT status FROM ordersদেখায় মানটা'delivered'নাকি'Delivered'। - প্রান্তগুলো যাচাই করুন। NULL, সমান মান, রেঞ্জের প্রথম আর শেষ দিন, এমন একটা সারি যা ফলাফলে থাকার (বা না থাকার) কথা আপনি জানেন।
একটা আসল বাগে এটা প্রয়োগ করা যাক। কাজটা: "Electronics বা Accessories-এর যে প্রোডাক্টগুলোর স্টক ১০-এর কম, যাতে আবার অর্ডার দেওয়া যায়।" প্রথম চেষ্টা:
SELECT name, category_id, stock
FROM products
WHERE stock < 10 AND category_id = 2 OR category_id = 4
ORDER BY product_id;+-----------------------------+-------------+-------+
| name | category_id | stock |
+-----------------------------+-------------+-------+
| 27-inch Monitor | 2 | 5 |
| Laptop Stand | 4 | 25 |
| USB-C Hub | 4 | 0 |
| Noise-Cancelling Headphones | 2 | 8 |
+-----------------------------+-------------+-------+প্রান্ত যাচাই: Laptop Stand-এর স্টক ২৫, তাই এর এখানে থাকার কথা নয়। কোয়েরি ছোট করে শুধু প্রথম শর্তে নিয়ে আসুন:
SELECT name, category_id, stock
FROM products
WHERE stock < 10
-- AND category_id = 2 OR category_id = 4
ORDER BY product_id;+-----------------------------+-------------+-------+
| name | category_id | stock |
+-----------------------------+-------------+-------+
| 27-inch Monitor | 2 | 5 |
| USB-C Hub | 4 | 0 |
| Noise-Cancelling Headphones | 2 | 8 |
+-----------------------------+-------------+-------+তিনটা সারি, আর সবগুলোই এমনিতেই Electronics বা Accessories। তাহলে প্রথম শর্ত ঠিক আছে, আর বাগটা যে লাইন বন্ধ করেছি সেখানে: AND আগে খাটে OR-এর চেয়ে, তাই OR category_id = 4 সব অ্যাকসেসরিজ টেনে এনেছে। ঠিক করুন, আর ফলাফল আগের ধাপের সাথে মিলে যায়:
SELECT name, category_id, stock
FROM products
WHERE stock < 10
AND category_id IN (2, 4)
ORDER BY product_id;+-----------------------------+-------------+-------+
| name | category_id | stock |
+-----------------------------+-------------+-------+
| 27-inch Monitor | 2 | 5 |
| USB-C Hub | 4 | 0 |
| Noise-Cancelling Headphones | 2 | 8 |
+-----------------------------+-------------+-------+UPDATE বা DELETE-এর আগে SELECT
পরিবর্তনের বেলায় রুটিনে আরেকটা নিয়ম আছে, আর এটাই ক্যারিয়ার বাঁচায়: আগে WHERE-টা একটা SELECT হিসেবে চালান। SELECT যদি প্রত্যাশিত সারিগুলোই ফেরত দেয়, তাহলে SELECT …-এর জায়গায় DELETE বসান, WHERE একই রেখে। ট্রানজ্যাকশনের ভেতরে থাকলে কয়টা সারি বদলাল তা যাচাইয়ের একটা শেষ সুযোগও পান:
from sqlhelp import con
# ১. আগে দেখুন: WHERE কোন সারিগুলোর সাথে মেলে?
print(con.execute("SELECT order_id, product_id FROM order_items WHERE order_id = 5").fetchall())
# ২. একই WHERE দিয়ে বদলান, তারপর commit-এর আগে সংখ্যাটা যাচাই করুন
cur = con.execute("DELETE FROM order_items WHERE order_id = 5")
print("rows deleted:", cur.rowcount)
# ৩. এটা শুধু ডেমো, তাই ফিরিয়ে নিন (আসল কাজে: সংখ্যা ঠিক থাকলে con.commit())
con.rollback()
print("rows left:", con.execute("SELECT COUNT(*) FROM order_items").fetchone()[0])[(5, 5)]
rows deleted: 1
rows left: 20rowcount যদি ১-এর বদলে ২০ বলত, আপনি rollback করে আবার দেখতেন; টিমকে ব্যাখ্যা করতে হতো না অর্ডারের ইতিহাস কেন উধাও। INSERT, UPDATE, DELETE আর আপসার্ট, নিরাপদে পাতায় এটাকে একটা পূর্ণ রুটিনে পরিণত করা হয়েছে।
সাধারণ ভুল
- এরর যে শব্দের নাম বলে, সেটাই সারানো।
near "FROM": syntax errorসাধারণত বোঝায় সমস্যাটাFROM-এর ঠিক আগে, যেমন একটা বাড়তি কমা। - "এরর নেই" মানে "ঠিক আছে" ধরে নেওয়া। হেডার সারি, সারির সংখ্যা, আর হাতে যাচাই করা যায় এমন একটা সারি দেখুন।
- একসাথে কয়েকটা জিনিস বদলানো। তখন জানবেন না কোন বদলটায় ঠিক হলো, বা কোনটা অন্য কিছু ভেঙে দিল। একটা জিনিস বদলান, চালান, দেখুন।
- আগে SELECT না করে DELETE বা UPDATE।
SELECT … WHERE <একই শর্ত>চালান, সারিগুলো দেখুন, তারপর বদলান। - SQLite-এর উদারতায় ভরসা। ডাবল কোটে লেখা স্ট্রিং, WHERE-এ অ্যালিয়াস, সংখ্যার কলামে টেক্সট: সব SQLite-এ "চলে", আর PostgreSQL-এ ভাঙে (বা ফিরিয়ে দেওয়া হয়)। SQLite-এ অনুশীলন করার সময়ও স্ট্যান্ডার্ড SQL লিখুন।
নিজে চেষ্টা করুন
- সহজ: এই কোয়েরিটা ব্যর্থ হয়:
SELECT name, price, FROM products ORDER BY price;। এররটা পড়ুন, তারপর সারান। - মাঝারি:
SELECT name FROM customers WHERE city = NULL;কিছুই ফেরত দেয় না, কোনো এররও দেয় না। কেন, ব্যাখ্যা করুন আর সারান। - মাঝারি: একটা Python ফাংশন
columns(table)লিখুন, যাPRAGMA table_infoদিয়ে একটা টেবিলের কলামের নামগুলোর লিস্ট ফেরত দেয়, আরorders-এর কলামগুলো প্রিন্ট করুন। - কঠিন: কাজটা হলো "Books (ক্যাটাগরি ১) বা Courses (ক্যাটাগরি ৩)-এর ৩০০০ টাকার কম দামের প্রোডাক্টের নাম আর দাম, সস্তাটা আগে"। এই কোয়েরিতে তিনটা বাগ আছে, কোনো এরর নেই:
SELECT name price FROM products WHERE category_id = 1 OR category_id = 3 AND price < 3000 ORDER BY price DESC;। তিনটাই খুঁজে বের করে সারান।
উত্তর
-- 1: এরর বলছে near "FROM"; তার আগের বাড়তি কমাটাই বাগ
SELECT name, price FROM products ORDER BY price;
-- 2: "= NULL" প্রতিটা সারির জন্য অজানা; NULL পরীক্ষা করুন IS দিয়ে
SELECT name FROM customers WHERE city IS NULL;from sqlhelp import con
# 3: PRAGMA table_info প্রতিটা কলামের জন্য একটা সারি দেয়; নামটা দ্বিতীয় ফিল্ডে
def columns(table):
return [row[1] for row in con.execute(f"PRAGMA table_info({table})")]
print(columns("orders"))-- 4: কমা নেই, AND/OR অগ্রাধিকার, ছোট থেকে বড়র বদলে DESC
SELECT name, price
FROM products
WHERE category_id IN (1, 3)
AND price < 3000
ORDER BY price, product_id;৪-এর ঠিক করা কোয়েরি দেয় Python Crash Course (১২০০) আর Hands-On Machine Learning (২৫০০)। SQL Masterclass-এর দাম ঠিক ৩০০০, তাই "৩০০০-এর কম" এটাকে বাদ দেয়; সীমানার কোনো সারি নিয়ে সন্দেহ হলে কাজের বর্ণনাটা আবার পড়ুন।
সারসংক্ষেপ
- সিনট্যাক্স এরর সেই শব্দ দেখায় যেখানে পার্সিং ব্যর্থ হয়েছে; ভুলটা সাধারণত তার ঠিক আগে।
- "টেবিল/কলাম নেই" মানে নামের সমস্যা:
PRAGMA table_info,\dবাDESCRIBEদিয়ে আসল নাম দেখুন, আর উদ্ধৃতিচিহ্ন যাচাই করুন। - কনস্ট্রেইন্ট এরর (UNIQUE, NOT NULL, CHECK, FOREIGN KEY) মানে ডেটাবেস আপনার ডেটা রক্ষা করছে। SQLite টাইপ নিয়ে উদার; PostgreSQL আর strict মোডের MySQL নয়।
- Python-এ
sqlite3.Errorধরুন আর মেসেজটা রাখুন: লগ আর LLM-এর সারাইয়ের লুপে এটা কাজে লাগে। - সবচেয়ে খারাপ বাগ কোনো এরর দেয় না। কোয়েরি ছোট করুন, টুকরোগুলো চালান, সারি গুনুন, প্রান্ত যাচাই করুন, আর প্রতিটা UPDATE বা DELETE-এর আগে SELECT করুন।
এরপর: ডেটা টাইপ, NULL আর ঠিক টাইপ বাছাই। এই পাতার কয়েকটা বাগ এসেছে টাইপ থেকে: সংখ্যার কলামে টেক্সট, পূর্ণসংখ্যার ভাগ, টেক্সট হিসেবে তারিখ। এবার দেখবেন প্রতিটা ডেটাবেস ঠিক কোন কোন টাইপ দেয়, আর কীভাবে সেগুলো বাছতে হয়।