অধ্যায় 1 · ডেটাবেস ও SQL
SQL কীভাবে কাজ করে: স্টেটমেন্ট, কমান্ডের ধরন আর কোয়েরির ক্রম
- পৃষ্ঠা 4 / 22
- 14 মিনিট পড়া
SQL একটা ছোট ভাষা, যার কয়েকটা ব্যাকরণের নিয়ম প্রতিটা কোয়েরি মেনে চলে। একবার এগুলো শিখে নিলে একগাদা বিভ্রান্তিকর এরর উধাও হয়ে যায়: "Dhaka" কেন এক ডেটাবেসে চলে আর আরেকটায় ব্যর্থ হয়, এইমাত্র বানানো অ্যালিয়াস কেন "unknown", একটা কমেন্ট কেন ফলাফল বদলে দেয়। এই পাতায় আছে সেই নিয়মগুলো, SQL কমান্ডের পাঁচটা পরিবার (সাথে আপনার প্রথম CREATE TABLE), আর ডেটাবেস আসলে কোন ক্রমে একটা কোয়েরি চালায় — যা আপনার লেখার ক্রম নয়।
AI-এর কাজে এই নিয়মগুলো আরও বেশি জরুরি: ল্যাঙ্গুয়েজ মডেলের লেখা SQL (text-to-SQL) প্রায়ই SQLite-এ চলে, তারপর PostgreSQL-এ গিয়ে ব্যর্থ হয় — ঠিক নিচের কারণগুলোর জন্য — আর সেটা ধরতে হবে আপনাকেই।
যা শিখবেন
- স্টেটমেন্ট আর সেমিকোলন, কিওয়ার্ড আর আইডেন্টিফায়ার, বড়-ছোট হাতের অক্ষর, হোয়াইটস্পেস আর কমেন্ট
- টেক্সটের জন্য সিঙ্গেল কোট আর নামের জন্য ডাবল কোট, আর এ ব্যাপারে SQLite, MySQL ও PostgreSQL কোথায় আলাদা
- কমান্ডের পাঁচটা পরিবার — DDL, DML, DQL, DCL, TCL — প্রতিটার একটা করে চালানো যায় এমন উদাহরণ
- সার্ভারে আর SQLite-এ ডেটাবেস বানানোর পার্থক্য, আর আপনার প্রথম
CREATE TABLE - কোয়েরির লজিক্যাল ক্রম: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
স্টেটমেন্ট আর সেমিকোলন
একটা স্টেটমেন্ট হলো একটা সম্পূর্ণ কমান্ড। সেমিকোলন সেটা শেষ করে, তাই একটা স্ক্রিপ্টে অনেক স্টেটমেন্ট থাকতে পারে। এই ব্লক তিনটা চালায়, আর আপনি পান তিনটা ফলাফল:
SELECT COUNT(*) AS customers FROM customers;
SELECT COUNT(*) AS products FROM products;
SELECT COUNT(*) AS orders FROM orders;+-----------+
| customers |
+-----------+
| 8 |
+-----------+
+----------+
| products |
+----------+
| 11 |
+----------+
+--------+
| orders |
+--------+
| 14 |
+--------+টুলভেদে কড়াকড়ি আলাদা। Python-এর execute() একটাই স্টেটমেন্ট নেয়, তাই সেখানে শেষে ; না দিলেও চলে। sqlite3, mysql আর psql শেল ; না দেখা পর্যন্ত কিছুই চালায় না — প্রম্পট যদি চুপচাপ বসে থাকে, সম্ভবত সেমিকোলন দিতে ভুলে গেছেন। তবে দুটো স্টেটমেন্টের মাঝে সেমিকোলন কখনোই ঐচ্ছিক নয়।
কিওয়ার্ড, আইডেন্টিফায়ার আর অক্ষরের কেস
একটা স্টেটমেন্ট তিন রকম শব্দে তৈরি: কিওয়ার্ড, যা SQL-এর নিজের (SELECT, FROM, WHERE); আইডেন্টিফায়ার, যা আপনার জিনিসের নাম (customers, city); আর লিটারাল, যা মান ('Dhaka', 42)। কিওয়ার্ডে বড়-ছোট হাতের অক্ষরে কিছু যায় আসে না, আর হোয়াইটস্পেস — স্পেস, ট্যাব, নতুন লাইন — যেমন খুশি দেওয়া যায়। এই অগোছালো কোয়েরিটাও দিব্যি চলে:
select NAME, City
from CUSTOMERS where customer_id = 2;+--------------+------------+
| name | city |
+--------------+------------+
| Tanvir Ahmed | Chattogram |
+--------------+------------+ডায়ালেক্টগুলো আলাদা হয় আইডেন্টিফায়ারে এসে:
| ডেটাবেস | কোট ছাড়া নাম | কোটসহ নাম |
|---|---|---|
| SQLite | কেস মানে না: CUSTOMERS = customers | এটাও কেস মানে না |
| PostgreSQL | ছোট হাতের করে নেয়: NAME হয়ে যায় name | হুবহু: "Name" আর name আলাদা কলাম |
| MySQL | কলাম কেস মানে না; টেবিলের নাম ফাইল সিস্টেম মেনে চলে — Linux-এ কেস মানে, Windows বা macOS-এ মানে না (ডিফল্টে) | ব্যাকটিক: `name` |
-- PostgreSQL
SELECT NAME FROM Customers WHERE customer_id = 1;
SELECT "Name" FROM customers WHERE customer_id = 1;+--------------+
| name |
+--------------+
| Nadia Rahman |
+--------------+
Error: column "Name" does not existপ্রথম কোয়েরিটা চলে, কারণ PostgreSQL NAME আর Customers-কে ছোট হাতের করে নেয়; দ্বিতীয়টা চায় হুবহু Name নামের একটা কলাম, যা নেই। MySQL-এর টেবিল-নামের নিয়ম তাঁদের বিপদে ফেলে, যাঁরা Windows-এ ডেভেলপ করে Linux-এ ডিপ্লয় করেন। পেশাদার রীতি এসবই এড়িয়ে যায়: কিওয়ার্ড বড় হাতের অক্ষরে, নাম ছোট হাতের snake_case-এ, প্রতি লাইনে একটা ক্লজ। এই টিউটোরিয়ালেও সেই স্টাইল।
কোট: 'টেক্সট' বনাম "নাম"
স্ট্যান্ডার্ড SQL-এর নিয়ম সহজ: সিঙ্গেল কোট মানে টেক্সট ('Dhaka'), ডাবল কোট মানে নাম ("order", "unit price") — দরকার শুধু তখন, যখন নামে স্পেস আছে, বড় হাতের অক্ষর হুবহু রাখতে চান, বা নামটা কোনো কিওয়ার্ডের সাথে মিলে যায়। টেক্সটের ভেতরে সিঙ্গেল কোট রাখতে সেটা দুবার লিখুন:
SELECT 'Rafiq''s order' AS label, "name" FROM customers WHERE customer_id = 4;+---------------+-------------+
| label | name |
+---------------+-------------+
| Rafiq's order | Rafiq Islam |
+---------------+-------------+এবার ফাঁদটা। পুরোনো কোডের সাথে মানিয়ে চলতে SQLite ডাবল কোটের একটা শব্দকে টেক্সট হিসেবে নেয়, যদি ওই নামে কোনো কলাম না থাকে; আর MySQL ডিফল্টভাবেই ডাবল কোটকে টেক্সট ধরে। তাই SQLite-এ এটা ঠিকঠাক চলে বলে মনে হয়:
SELECT name FROM customers WHERE city = "Dhaka" ORDER BY name;
-- একই অভ্যাস, কিন্তু "টেক্সট"-টা ঘটনাক্রমে একটা কলামের নাম
SELECT COUNT(*) AS customers_in_city FROM customers WHERE city = "city";+---------------+
| name |
+---------------+
| Farhana Akter |
| Mitu Das |
| Nadia Rahman |
+---------------+
+-------------------+
| customers_in_city |
+-------------------+
| 7 |
+-------------------+প্রথম কোয়েরিটা চলেছে কপালগুণে। দ্বিতীয়টায় "city" হলো কলাম, তাই শর্তটা দাঁড়ায় city = city, যা শহর আছে এমন প্রত্যেক কাস্টমারের জন্য সত্য: ০-এর বদলে ৭টা সারি, আর কোনো এরর নেই। PostgreSQL স্ট্যান্ডার্ড মেনে চলে, তাই প্রথমটাকেই সরাসরি বাতিল করে দেয়:
-- PostgreSQL
SELECT name FROM customers WHERE city = "Dhaka" ORDER BY name;Error: column "Dhaka" does not existনিয়ম: টেক্সটের জন্য সবসময় সিঙ্গেল কোট। নামের জন্য MySQL ব্যবহার করে ব্যাকটিক (`order`), SQL Server স্কয়ার ব্র্যাকেট ([order]); SQLite তিনটাই মেনে নেয়।
কমেন্ট
দুই রকম কমেন্ট সব জায়গায় চলে: লাইনের শেষ পর্যন্ত --, আর কয়েক লাইন জুড়ে /* … */। কোয়েরি কোনো কাজ কেন করছে, সেটা বোঝাতে এগুলো ব্যবহার করুন:
/* আবার অর্ডার দেওয়ার মতো প্রোডাক্ট:
স্টক শেষ, বা কমে আসছে। */
SELECT name, stock
FROM products
WHERE stock < 10 -- NULL স্টক (কোর্স, গিফট কার্ড) "কম" নয়
ORDER BY stock, name;+-----------------------------+-------+
| name | stock |
+-----------------------------+-------+
| USB-C Hub | 0 |
| 27-inch Monitor | 5 |
| Noise-Cancelling Headphones | 8 |
+-----------------------------+-------+MySQL-এর একটা অদ্ভুত নিয়ম আছে: -- কমেন্ট শুরু করে কেবল তখনই, যখন পরে একটা স্পেস থাকে (এটা #-ও মানে)। স্পেস না থাকলে --2 মানে "মাইনাস মাইনাস 2":
SELECT 5 --2
AS surprise;+----------+
| surprise |
+----------+
| 5 |
+----------+-- MySQL
SELECT 5 --2
AS surprise;+----------+
| surprise |
+----------+
| 7 |
+----------+SQLite --2-কে কমেন্ট ধরে 5 দিয়েছে; MySQL হিসাব করেছে 5 − (−2) = 7। ---এর পরে সবসময় একটা স্পেস দিন।
কমান্ডের পাঁচটা পরিবার
কিসের ওপর কাজ করে, সেই অনুযায়ী SQL স্টেটমেন্ট পাঁচ ভাগে পড়ে:
| পরিবার | পুরো নাম | কাজ করে যার ওপর | প্রধান কমান্ড |
|---|---|---|---|
| DDL | Data Definition Language | গঠন: টেবিল, কলাম, ইনডেক্স | CREATE, ALTER, DROP |
| DML | Data Manipulation Language | টেবিলের ভেতরের সারি | INSERT, UPDATE, DELETE |
| DQL | Data Query Language | ডেটা পড়া | SELECT |
| DCL | Data Control Language | কে কী করতে পারবে | GRANT, REVOKE |
| TCL | Transaction Control Language | কয়েকটা বদলকে এক প্যাকেটে বাঁধা: হয় সব হবে, নয় কিছুই না | BEGIN, COMMIT, ROLLBACK |
(কিছু বই SELECT-কে DML-এ ধরে। লেবেলের চেয়ে জরুরি হলো জানা, কোন কমান্ড গঠন বদলায়, কোনটা ডেটা বদলায়, আর কোনটা শুধু পড়ে।) নিচে প্রতিটার একটা করে উদাহরণ, একটা reviews টেবিল বানিয়ে — এমন টেক্সট, যা পরে হয়তো একটা সেন্টিমেন্ট মডেলে ইনপুট হিসেবে দেবেন।
DDL: আপনার প্রথম CREATE TABLE
CREATE TABLE reviews (
review_id INTEGER PRIMARY KEY,
product_id INTEGER NOT NULL,
customer_id INTEGER NOT NULL,
rating INTEGER NOT NULL CHECK (rating BETWEEN 1 AND 5),
body TEXT,
FOREIGN KEY (product_id) REFERENCES products (product_id),
FOREIGN KEY (customer_id) REFERENCES customers (customer_id)
);বন্ধনীর ভেতরের প্রতিটা লাইন হয় একটা কলাম — একটা নাম, একটা টাইপ, তারপর ঐচ্ছিক নিয়ম (কনস্ট্রেইন্ট) — নয়তো পুরো টেবিলের একটা নিয়ম। PRIMARY KEY আর FOREIGN KEY … REFERENCES হলো টেবিল, কি আর রিলেশনশিপ পাতার সেই কি-গুলো; NOT NULL ফাঁকা মান নিষেধ করে; CHECK ১–৫-এর বাইরের রেটিং আটকে দেয়। সবগুলো বিস্তারিত আছে CREATE, ALTER, DROP আর কনস্ট্রেইন্ট পাতায়।
ফরেন কি লিখুন আলাদা
FOREIGN KEY (col) REFERENCES table (col)লাইনে, যেমন এখানে আরshop.sql-এ। কলামের গায়েই লেখা ছোট রূপটা (product_id INTEGER REFERENCES products (product_id)) SQLite আর PostgreSQL-এ চলে, কিন্তু MySQL 8 সেটা মেনে নিয়ে চুপচাপ উপেক্ষা করে — কোনো ফরেন কি তৈরিই হয় না।
DML: সারি ঢোকান
INSERT INTO reviews (review_id, product_id, customer_id, rating, body) VALUES
(1, 1, 1, 5, 'Clear and practical, perfect first book'),
(2, 3, 6, 4, 'Great keys, a bit loud');DQL: আবার পড়ুন
SELECT review_id, product_id, rating, body
FROM reviews
ORDER BY review_id;+-----------+------------+--------+-----------------------------------------+
| review_id | product_id | rating | body |
+-----------+------------+--------+-----------------------------------------+
| 1 | 1 | 5 | Clear and practical, perfect first book |
| 2 | 3 | 4 | Great keys, a bit loud |
+-----------+------------+--------+-----------------------------------------+TCL: হয় সব, নয় কিছুই না
একটা ট্রানজ্যাকশন কয়েকটা স্টেটমেন্টকে এমনভাবে বাঁধে যে হয় সবগুলো কার্যকর হয়, নয়তো একটাও না। BEGIN একটা শুরু করে; ROLLBACK তারপর থেকে সবকিছু বাতিল করে; COMMIT করলে সেটা থেকে যেত:
BEGIN;
DELETE FROM reviews;
SELECT COUNT(*) AS reviews_inside_transaction FROM reviews;
ROLLBACK;
SELECT COUNT(*) AS reviews_after_rollback FROM reviews;+----------------------------+
| reviews_inside_transaction |
+----------------------------+
| 0 |
+----------------------------+
+------------------------+
| reviews_after_rollback |
+------------------------+
| 2 |
+------------------------+DCL: অনুমতি (শুধু সার্ভারে)
SQLite-এ কোনো ইউজার নেই — যে ফাইলটা খুলতে পারে, সে-ই পড়তে পারে। সার্ভার ডেটাবেসে অ্যাকাউন্ট আর রোল থাকে, আর কে কী করতে পারবে তা ঠিক করে GRANT। একটা অ্যানালিটিক্স ড্যাশবোর্ড বা LLM এজেন্টের জন্য শুধু-পড়ার একটা রোল, PostgreSQL-এ:
-- PostgreSQL
DROP ROLE IF EXISTS shop_reader;
CREATE ROLE shop_reader;
GRANT SELECT ON customers TO shop_reader;
SELECT grantee, table_name, privilege_type
FROM information_schema.role_table_grants
WHERE grantee = 'shop_reader';
-- শেষে পরিষ্কার করে ফেলা
REVOKE SELECT ON customers FROM shop_reader;
DROP ROLE shop_reader;+-------------+------------+----------------+
| grantee | table_name | privilege_type |
+-------------+------------+----------------+
| shop_reader | customers | SELECT |
+-------------+------------+----------------+রোলটা শুধু customers পড়তে পারে, আর কিছু না। মালিকের অ্যাকাউন্টের বদলে একটা AI এজেন্টকে এমন একটা রোল দেওয়াই text-to-SQL অ্যাপে সুরক্ষার প্রথম ধাপ; অ্যাডভান্সড টিউটোরিয়ালে এটাকে আরও এগিয়ে নেওয়া হবে।
ডেটাবেস বানানো: সার্ভার বনাম ফাইল
একটা ডেটাবেস সার্ভারে অনেক ডেটাবেস থাকে, তাই নতুন একটা বানাতে হয় SQL দিয়ে। MySQL-এ DATABASE আর SCHEMA একই জিনিস:
-- MySQL
CREATE DATABASE bitbyte_practice;
SHOW DATABASES LIKE 'bitbyte_practice';
DROP DATABASE bitbyte_practice;+-----------------------------+
| Database (bitbyte_practice) |
+-----------------------------+
| bitbyte_practice |
+-----------------------------+-- PostgreSQL
CREATE DATABASE bitbyte_practice;
SELECT datname FROM pg_database WHERE datname = 'bitbyte_practice';
DROP DATABASE bitbyte_practice;+------------------+
| datname |
+------------------+
| bitbyte_practice |
+------------------+নতুন ডেটাবেসের ভেতরে কাজ করতে সেখানে যেতে হয়: MySQL-এ USE bitbyte_practice;, PostgreSQL-এ নতুন একটা কানেকশন (psql-এ \c bitbyte_practice)। PostgreSQL-এ একটা ডেটাবেসের ভেতরে আবার স্কিমা থাকে (ডিফল্টটা public) — এই স্তরটা MySQL বা SQLite-এ নেই।
SQLite-এ কোনো CREATE DATABASE নেই। ডেটাবেস মানে একটা ফাইল, আর নেই এমন ফাইলে কানেক্ট করলেই সেটা তৈরি হয়ে যায়:
import os
import sqlite3
print("before:", os.path.exists("practice.db"))
db = sqlite3.connect("practice.db")
db.execute("CREATE TABLE notes (note_id INTEGER PRIMARY KEY, body TEXT)")
db.close()
print("after: ", os.path.exists("practice.db"))before: False
after: TrueSQLite ডেটাবেস মুছতে ফাইলটা মুছে দিন। (একটা কানেকশনের ভেতরে SQLite দ্বিতীয় একটা ফাইলও খুলতে পারে, ATTACH DATABASE 'practice.db' AS practice; দিয়ে — এক ফাইল থেকে আরেক ফাইলে ডেটা কপি করতে কাজের।)
লেখার ক্রম বনাম লজিক্যাল ক্রম
এক টেবিলের কোয়েরির সব প্রধান ক্লজ আছে এই কোয়েরিতে — "শুধু ২০০০ টাকার বেশি দামের প্রোডাক্ট গুনলে কোন ক্যাটাগরিতে সবচেয়ে বেশি (দুটোর কম আছে এমন ক্যাটাগরি বাদে)?":
SELECT category_id, COUNT(*) AS n
FROM products
WHERE price > 2000
GROUP BY category_id
HAVING COUNT(*) >= 2
ORDER BY n DESC, category_id
LIMIT 1;+-------------+---+
| category_id | n |
+-------------+---+
| 2 | 3 |
+-------------+---+আপনি SELECT লেখেন সবার আগে, কিন্তু ডেটাবেস সেটা চালায় প্রায় সবার শেষে। লজিক্যাল ক্রমই ঠিক করে কোন ক্লজ কী দেখতে পায়:
লেখার ক্রম লজিক্যাল ক্রম এই ধাপে যা ঘটে
------------- ------------------- -------------------------------------------
1 SELECT 1 FROM products টেবিলের 11টা সারিই নেওয়া
2 FROM 2 WHERE price > 2000 শর্ত মেলে এমন সারি রাখা -> 7টা সারি
3 WHERE 3 GROUP BY প্রতি category_id-তে একটা দল -> 4টা দল
4 GROUP BY 4 HAVING 2 বা তার বেশি সদস্যের দল রাখা -> 3টা দল
5 HAVING 5 SELECT আউটপুট কলাম হিসাব হয়; অ্যালিয়াস n-এর জন্ম
6 ORDER BY 6 ORDER BY সাজানো; এখানে অ্যালিয়াস n ব্যবহার করা যায়
7 LIMIT 7 LIMIT প্রথম সারিটা রাখা -> 1টা সারি(কোয়েরি প্ল্যানার বাস্তবে আরও চালাক ক্রমে কাজ করতে পারে, কিন্তু ফলাফল সবসময় হয় যেন এই ধাপগুলো মেনেই হয়েছে।) এর সবচেয়ে কাজের ফল: SELECT-এ (ধাপ ৫) বানানো অ্যালিয়াস WHERE-এ (ধাপ ২) তখনো জন্মায়নি, কিন্তু ORDER BY-তে (ধাপ ৬) আছে:
-- PostgreSQL
SELECT name, price * 2 AS double_price
FROM products
WHERE double_price > 20000
ORDER BY double_price DESC;Error: column "double_price" does not exist-- MySQL
SELECT name, price * 2 AS double_price
FROM products
WHERE double_price > 20000
ORDER BY double_price DESC;Error: ERROR 1054: Unknown column 'double_price' in 'where clause'SQLite এখানে ছাড় দেয়, এই কোয়েরি মেনে নেয় — আর ঠিক এ কারণেই শুধু SQLite-এ পরীক্ষা করা কোড (বা LLM-এর লেখা SQL) প্রোডাকশনে গিয়ে ভাঙে। সব জায়গায় চলে এমন সমাধান হলো WHERE-এ এক্সপ্রেশনটা আবার লেখা; ORDER BY-তে অ্যালিয়াস ব্যবহার সব জায়গাতেই ঠিক আছে:
-- PostgreSQL
SELECT name, price * 2 AS double_price
FROM products
WHERE price * 2 > 20000
ORDER BY double_price DESC;+-------------------------+--------------+
| name | double_price |
+-------------------------+--------------+
| 27-inch Monitor | 37000 |
| AI Engineering Bootcamp | 30000 |
+-------------------------+--------------+সাধারণ ভুল
- টেক্সটের চারপাশে ডাবল কোট। SQLite আর MySQL-এ চলে, PostgreSQL-এ ব্যর্থ হয়, আর শব্দটা কোনো কলামের নামও হলে চুপচাপ মানে বদলে যায়। সিঙ্গেল কোট ব্যবহার করুন:
SELECT COUNT(*) AS dhaka_customers FROM customers WHERE city = 'Dhaka';+-----------------+ | dhaka_customers | +-----------------+ | 3 | +-----------------+ - দুটো স্টেটমেন্টের মাঝে সেমিকোলন না দেওয়া। ডেটাবেস দুটোকে একটা ভাঙা স্টেটমেন্ট হিসেবে পড়ে:
SELECT name FROM categories WHERE category_id = 1 SELECT name FROM categories WHERE category_id = 2;সমাধান: প্রতিটা স্টেটমেন্টError: near "SELECT": syntax error;দিয়ে শেষ করুন। - কিওয়ার্ডকে নাম হিসেবে ব্যবহার।
orderএকটা কিওয়ার্ড (ORDER BY), তাই এটা ব্যর্থ হয়:CREATE TABLE order (order_id INTEGER PRIMARY KEY);সমাধান: সাধারণ একটা নাম বেছে নিন (Error: near "order": syntax errororders, যেমন দোকানে আছে);"order"লিখে কোট করলে চলে, কিন্তু ভবিষ্যতের প্রতিটা কোয়েরি ঝামেলার হয়ে যায়। SELECT-এর অ্যালিয়াসWHERE-এ ব্যবহার। শুধু SQLite-এ চলে। ওপরে যেমন দেখানো হয়েছে, এক্সপ্রেশনটা আবার লিখুন।- স্পেস ছাড়া
--comment। SQLite আর PostgreSQL-এ নিরীহ, MySQL-এ হয়ে যায় বিয়োগের হিসাব। লিখুন-- comment।
নিজে চেষ্টা করুন
- সহজ: প্রতিটা কোন পরিবারের (DDL, DML, DQL, DCL না TCL) বলুন: (ক)
DROP TABLE reviews;(খ)UPDATE products SET stock = 30 WHERE product_id = 1;(গ)COMMIT;(ঘ)REVOKE SELECT ON customers FROM shop_reader;(ঙ)SELECT * FROM orders; - মাঝারি: একটা ল্যাঙ্গুয়েজ মডেলের কল লগ করার জন্য
llm_callsটেবিল বানান:call_id(প্রাইমারি কি),model(টেক্সট, আবশ্যক),prompt_tokensআরcompletion_tokens(পূর্ণসংখ্যা, আবশ্যক), আরrating(১ থেকে ৫, ঐচ্ছিক)। তিনটা কল ঢুকিয়ে হিসাব করা একটাtotal_tokensকলামসহ সেগুলো SELECT করুন। - কঠিন: এই কোয়েরিটা SQLite-এ চলে, কিন্তু PostgreSQL-এ ব্যর্থ হয়। লজিক্যাল ক্রম দিয়ে কারণটা ব্যাখ্যা করুন, তারপর ঠিক করে PostgreSQL-এ চালান:
SELECT name, price * stock AS stock_value FROM products WHERE stock_value > 50000 ORDER BY stock_value DESC, name;
উত্তর
১. (ক) DDL, (খ) DML, (গ) TCL, (ঘ) DCL, (ঙ) DQL।
২. একটা সম্ভাব্য টেবিল; rating NULL হতে পারে, কারণ প্রতিটা কলের রেটিং হয় না:
CREATE TABLE llm_calls (
call_id INTEGER PRIMARY KEY,
model TEXT NOT NULL,
prompt_tokens INTEGER NOT NULL,
completion_tokens INTEGER NOT NULL,
rating INTEGER CHECK (rating BETWEEN 1 AND 5)
);
INSERT INTO llm_calls VALUES
(1, 'model-small', 420, 180, 4),
(2, 'model-large', 950, 610, 5),
(3, 'model-small', 300, 95, NULL);
SELECT call_id, model, prompt_tokens + completion_tokens AS total_tokens, rating
FROM llm_calls
ORDER BY call_id;+---------+-------------+--------------+--------+
| call_id | model | total_tokens | rating |
+---------+-------------+--------------+--------+
| 1 | model-small | 600 | 4 |
| 2 | model-large | 1560 | 5 |
| 3 | model-small | 395 | NULL |
+---------+-------------+--------------+--------+৩. SELECT (ধাপ ৫) stock_value বানানোর আগেই WHERE (ধাপ ২) চলে, তাই PostgreSQL তখনো নামটা চেনে না। WHERE-এ এক্সপ্রেশনটা আবার লিখুন; ORDER BY-তে অ্যালিয়াস রাখুন। NULL স্টকের প্রোডাক্টগুলো বাদ পড়ে, কারণ NULL × দাম = NULL:
-- PostgreSQL
SELECT name, price * stock AS stock_value
FROM products
WHERE price * stock > 50000
ORDER BY stock_value DESC, name;+-----------------------------+-------------+
| name | stock_value |
+-----------------------------+-------------+
| 27-inch Monitor | 92500 |
| Noise-Cancelling Headphones | 60000 |
| Mechanical Keyboard | 54000 |
| Wireless Mouse | 54000 |
+-----------------------------+-------------+সারসংক্ষেপ
- স্টেটমেন্ট শেষ হয়
;দিয়ে। কিওয়ার্ডে কেস মানে না, হোয়াইটস্পেস যেমন খুশি; কিওয়ার্ড লিখুন বড় হাতের অক্ষরে, নাম ছোট হাতের। - টেক্সটের জন্য সিঙ্গেল কোট, নামের জন্য ডাবল কোট। SQLite আর MySQL
"text"মেনে নেয়, PostgreSQL নেয় না, আর SQLite-এ এটা চুপচাপ একটা কলামকে তার নিজের সাথেই তুলনা করে বসতে পারে। - কমেন্ট হলো
-- …আর/* … */; MySQL-এ---এর পরে একটা স্পেস দিন। - DDL গঠন বদলায়, DML সারি বদলায়, DQL পড়ে, DCL অনুমতি নিয়ন্ত্রণ করে (শুধু সার্ভারে), TCL বদলগুলোকে ট্রানজ্যাকশনে বাঁধে।
- সার্ভারে লাগে
CREATE DATABASE; SQLite-এ ডেটাবেস হলো একটা ফাইল, যা কানেক্ট করলেই তৈরি হয়। - লজিক্যাল ক্রম: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT। অ্যালিয়াসের জন্ম
SELECT-এ, তাই সেটা ব্যবহার করুনORDER BY-তে,WHERE-এ নয়।
এরপর: SELECT: কলাম, এক্সপ্রেশন, অ্যালিয়াস আর DISTINCT পাতায় দেখবেন সেই ক্লজটা, যা আপনি সবার আগে লেখেন কিন্তু ডেটাবেস চালায় পঞ্চম ধাপে — আর এটা কী কী পারে: কলাম বাছাই, নতুন কলাম হিসাব করা, সেগুলোর নাম দেওয়া, আর ডুপ্লিকেট সরানো।