সরাসরি পর্বে যান
পর্ব ৩৪ · ৬৭-এর মধ্যে · মডিউল ৫

Relational calculus — tuple আর domain

Relational Calculus — Tuple & Domain Calculus, Safety, QBE, Codd's Theorem and the Logical Roots of SQL

Taught in Bangla, this lesson explains relational calculus as a declarative 'what, not how' language: first-order logic refreshers, tuple relational calculus {t | P(t)}, negation and universal quantifiers for 'none' and 'all', safety and infinite answers, domain relational calculus and QBE, Codd's theorem on equal expressive power, how SQL's FROM, WHERE and EXISTS come from TRC, and the limits of relational completeness.

এই পর্বে যা শিখবেন

  • Procedural আর declarative query ভাষার পার্থক্য, আর first-order logic-এর predicate, connective, quantifier, free আর bound variable
  • Tuple relational calculus-এ প্রশ্ন লেখা — atom, সূত্র আর ∀-এর ¬∃…¬ কৌশল — আর সূত্রটি সারি ধরে ধরে মূল্যায়ন করা
  • Safe আর unsafe expression, expression-এর domain, আর কেন SQL স্বভাবতই প্রায় safe
  • Domain relational calculus, QBE, আর Codd-এর উপপাদ্যের বক্তব্য ও প্রমাণের ধারণা
  • TRC, algebra আর SQL-এর মধ্যে অনুবাদ, আর pure calculus-এর সীমা: recursion আর aggregate

১ · কীভাবে বনাম কী — রাস্তা বলা আর ঠিকানা দেওয়া

ধরুন ঢাকায় নতুন এসেছেন, যাবেন এক আত্মীয়ের বাসায়। একজন বললেন, “ফার্মগেট থেকে বাসে উঠে কারওয়ান বাজার নামবেন, তারপর বাঁয়ে হেঁটে দ্বিতীয় গলি, তারপর ডানে তিন নম্বর বাড়ি।” আরেকজন শুধু ঠিকানাটি দিলেন: “বাড়ি ১২, রোড ৫, ধানমন্ডি।” প্রথমজন দিলেন রাস্তা — কোন ক্রমে কী করতে হবে। দ্বিতীয়জন দিলেন গন্তব্য — কোথায় পৌঁছাতে হবে, পথ আপনার বা রিকশাওয়ালার ব্যাপার। ঠিকানা পেলে রিকশাওয়ালা জ্যাম দেখে নিজেই সবচেয়ে ভালো পথ বেছে নেন।

পর্ব ৩২ আর ৩৩-এ দেখা relational algebra প্রথম ধরনের: “আগে students-এর ওপর σ চালাও, তারপর enrollments-এর সাথে ⋈, তারপর π.” অপারেটরগুলোর ক্রমই উত্তরের রেসিপি। এমন ভাষাকে বলে procedural. Relational calculus দ্বিতীয় ধরনের: এটি শুধু বলে উত্তরে কোন সারিগুলো থাকবে — “এমন সব শিক্ষার্থী, যাঁদের এমন একটি ভর্তি আছে যার কোর্স CSE301.” কোন টেবিল আগে পড়া হবে, কোথায় join হবে, তা বলে না। এটি declarative ভাষা।

বিশ্ববিদ্যালয়ের রেজিস্ট্রার অফিসে দুই রকম নোট আসে। একটিতে লেখা: “রেজিস্টার খুলে CSE-র পাতাগুলো আলাদা করুন, প্রতিটি নাম ভর্তির খাতায় মেলান, যাঁদের নামের পাশে CSE301 আছে তাঁদের তালিকা করুন।” আরেকটিতে লেখা: “CSE301-এ ভর্তি থাকা সব শিক্ষার্থীর নাম চাই।” দ্বিতীয় নোটটি পেলে অভিজ্ঞ কর্মচারী নিজের কৌশল খাটান — হয়তো উল্টো দিক থেকে, CSE301-এর ভর্তি খাতা আগে খুলে। DBMS-এর query optimizer ঠিক সেই অভিজ্ঞ কর্মচারী।

তাহলে calculus শিখব কেন, SQL তো আছেই? তিনটি কারণ। প্রথমত, SQL-এর গঠন সরাসরি tuple calculus থেকে এসেছে — FROM, WHERE আর EXISTS আসলে যুক্তিবিদ্যার চিহ্নের ইংরেজি রূপ। দ্বিতীয়ত, “সব কোর্স নিয়েছেন এমন শিক্ষার্থী” ধরনের প্রশ্ন SQL-এ লিখতে গেলে যুক্তির ভাষায় ভাবতেই হয়। তৃতীয়ত, ১৯৭২ সালে E. F. Codd দেখান যে algebra আর calculus ঠিক সমান শক্তিশালী। এই প্রমাণ থেকেই আসে relational completeness-এর ধারণা, যা দিয়ে আজও যেকোনো query ভাষার শক্তি মাপা হয়। বিশ্ববিদ্যালয়ের ডেটাবেস কোর্সে আর চাকরির লিখিত পরীক্ষায় TRC/DRC-তে প্রশ্ন লেখা একটি পরিচিত বিষয়।

এই পর্বের সব উদাহরণ চলে পরিচিত db-uni ডেটাবেসে। পাঁচটি টেবিল: departments (dept_id, name, building — 1 CSE, 2 EEE, 3 BBA, 4 English), instructors (instructor_id, name, dept_id, salary, mentor_id — mentor_id একই টেবিলের আরেকজন শিক্ষক), students (student_id, name, dept_id, batch, cgpa — ৮ জন; Habib-এর বিভাগ NULL, Fahim-এর cgpa NULL), courses (course_id, title, dept_id, credits, instructor_id — ৭টি কোর্স) আর enrollments (student_id, course_id, term, grade — ১৩টি ভর্তি). প্রতিটি এডিটরের নিচে “নমুনা ডেটা” বাক্সে পুরো টেবিল দেখতে পাবেন।

২ · First-order logic ঝালাই — predicate, connective আর quantifier

Relational calculus আসলে first-order logic (predicate logic), শুধু টেবিলের ওপর প্রয়োগ করা। পাঁচটি উপাদান জানলেই চলবে। আরও বিস্তারিত চাইলে ABCL TECH-এর Discrete Math কোর্সের predicate logic পর্ব দেখুন।

  • Predicate: এমন একটি বাক্য, যা চলকের মান বসালে সত্য বা মিথ্যা হয়। “x-এর cgpa 3.5-এর বেশি” — x = Arif হলে সত্য, x = Bristy হলে মিথ্যা। একটি টেবিলও একটি predicate: t ∈ students সত্য, যদি t সারিটি students টেবিলে থাকে।
  • Connective: ∧ (এবং, AND), ∨ (অথবা, OR), ¬ (নয়, NOT) আর → (implication, “যদি … তবে …”). P → Q মিথ্যা শুধু তখন, যখন P সত্য কিন্তু Q মিথ্যা। তাই P → Q ≡ ¬P ∨ Q.
  • Quantifier: ∃x মানে “অন্তত একটি x আছে, যার জন্য …”, আর ∀x মানে “প্রতিটি x-এর জন্য …”.
  • দুই quantifier-এর সম্পর্ক: “সবাই পাস করেছে” মানে “এমন কেউ নেই যে পাস করেনি”। প্রতীকে ∀x P(x) ≡ ¬∃x ¬P(x). এটি আর De Morgan-এর নিয়ম ¬(P ∨ Q) ≡ ¬P ∧ ¬Q, ¬(P ∧ Q) ≡ ¬P ∨ ¬Q — এই পর্বের সবচেয়ে কাজের অস্ত্র।
  • Free আর bound variable: কোনো quantifier যে চলককে “বেঁধে” রাখে, সেটি bound; যে চলককে কেউ বাঁধেনি, সেটি free. সূত্রের সত্য-মিথ্যা নির্ভর করে শুধু free চলকের মানের ওপর।

শেষ ধারণাটি calculus-এ সবচেয়ে গুরুত্বপূর্ণ, কারণ {t | P(t)}-এ “|”-এর বাঁ পাশের t-কে অবশ্যই P-এর একমাত্র free চলক হতে হয়। একটি সূত্রে কোনটি free আর কোনটি bound, ধাপে ধাপে দেখুন:

সূত্রলাল = এই ধাপে যা দেখছি

            
চলকের অবস্থা
এই ধাপে দেখছি
free
bound
  1. একটি সূত্র। বাংলায়: “t সারিটি students-এ আছে, আর এমন একটি ভর্তি e আছে যার student_id t-এর সমান আর কোর্স CSE301.” এখানে দুটি চলক: t আর e.
  2. t. t ∈ students-এ t ব্যবহার হয়েছে, কিন্তু t-এর আগে কোথাও ∃t বা ∀t নেই। তাই t free — এর মান বাইরে থেকে বসাতে হবে।
  3. e. ∃e e-কে বেঁধেছে। তার scope হলো ঠিক পরের বন্ধনীটুকু; বন্ধনীর ভেতরের সব e এই ∃-এর। e bound — “এমন একটি e আছে কি?” প্রশ্নের উত্তর দিলেই e-এর কাজ শেষ।
  4. Scope-এর ভেতরের t. t.student_id ∃e-এর বন্ধনীর ভেতরে, কিন্তু ∃e শুধু e-কে বাঁধে, t-কে নয়। তাই t এখানেও free. SQL-এ এটিই correlated subquery-র “বাইরের রেফারেন্স” (পর্ব ২১).
  5. সিদ্ধান্ত। free চলক একটিই, t — তাই এটি {t | …} আকারের বৈধ query. যে সূত্রে e free অথচ বাঁ পাশে শুধু t, সেটি অর্থহীন: e-এর মান কে দেবে?

৩ · Tuple relational calculus — {t | P(t)}

Tuple relational calculus (TRC)-এ প্রতিটি প্রশ্নের আকার একটিই:

{ t | P(t) } — “এমন সব tuple t, যার জন্য P(t) সত্য”

এখানে t একটি tuple variable — এর মান একটি পুরো সারি। t.A লিখলে বোঝায় t সারির A attribute-এর মান। P একটি সূত্র (formula), যা ছোট ছোট atomic formula (atom) দিয়ে গড়া। TRC-তে atom তিন রকম:

Atomঅর্থউদাহরণ
t ∈ Rt সারিটি R টেবিলে আছেt ∈ students
t.A θ s.Bদুই সারির দুটি মানের তুলনা; θ হলো =, ≠, <, ≤, >, ≥-এর একটিe.student_id = t.student_id
t.A θ cএকটি মানের সাথে একটি ধ্রুবকের তুলনাt.cgpa > 3.5

সূত্র গড়ার নিয়ম: প্রতিটি atom একটি সূত্র; P আর Q সূত্র হলে ¬P, P ∧ Q, P ∨ Q, P → Q সূত্র; আর P(s) সূত্র হলে ∃s (P(s)) আর ∀s (P(s)) সূত্র। সংক্ষেপে ∃s ∈ R (P) লেখা হয়, যার মানে ∃s (s ∈ R ∧ P); আর ∀s ∈ R (P) মানে ∀s (s ∈ R → P). খেয়াল করুন, ∃-এর সাথে ∧ আর ∀-এর সাথে → বসে — অংশ ৪-এ এই পার্থক্যই কাজে লাগবে।

প্রথম দুটি প্রশ্ন

“cgpa 3.5-এর বেশি এমন শিক্ষার্থী” — পুরো সারি চাইলে {t | t ∈ students ∧ t.cgpa > 3.5}. শুধু নাম চাইলে ফলের সারিটি নতুন, তাতে কেবল name আছে। তাই আরেকটি চলক s দিয়ে students-এর সারি খুঁজে t-এর name তার সাথে মেলাতে হয়: {t | ∃s ∈ students (s.cgpa > 3.5 ∧ t.name = s.name)}. পর্ব ৩২-এর σ আর π এখানে লুকিয়ে আছে — শর্তটি σ, আর “t.name = s.name, বাকি কলাম বাদ” হলো π. অনেক বইয়ে (যেমন Elmasri–Navathe) দ্বিতীয়টি সংক্ষেপে লেখা হয় {t.name | STUDENT(t) ∧ t.cgpa > 3.5} — অর্থ একই।

-- {t | t ∈ students ∧ t.cgpa > 3.5}
SELECT t.* FROM students AS t
WHERE t.cgpa > 3.5;

-- {t | ∃s ∈ students (s.cgpa > 3.5 ∧ t.name = s.name)}
SELECT s.name FROM students s
WHERE s.cgpa > 3.5;

প্রথম ফলে তিনটি পুরো সারি: 2101 | Arif Hossain | 1 | 2021 | 3.75, 2201 | Dilruba Khatun | 1 | 2022 | 3.9, 2301 | Gulshan Ara | 1 | 2023 | 3.55. দ্বিতীয় ফলে শুধু তিনটি নাম: Arif Hossain, Dilruba Khatun, Gulshan Ara. SQL-এর FROM students AS t ঠিক “t চলকটি students-এর সারিগুলোর ওপর ঘোরে”, আর WHERE হলো সূত্রটি। Fahim-এর cgpa NULL, তাই NULL > 3.5 UNKNOWN — তিনি বাদ। Pure calculus-এ NULL নেই, শুধু সত্য আর মিথ্যা; এই পার্থক্য অংশ ৪-এ একটি ফাঁদ তৈরি করবে।

সারি ধরে ধরে মূল্যায়ন — CSE301-এর শিক্ষার্থী

এবার দুই টেবিলের প্রশ্ন: “CSE301-এ ভর্তি হয়েছেন এমন শিক্ষার্থীর নাম”।

{ t | ∃s ∈ students (
        t.name = s.name
      ∧ ∃e ∈ enrollments ( e.student_id = s.student_id
                         ∧ e.course_id = 'CSE301' ) ) }

Calculus বলে না কীভাবে উত্তর বের করতে হবে। তবু অর্থ বোঝার সবচেয়ে সোজা উপায় হলো সংজ্ঞা ধরে হাতে মূল্যায়ন: students-এর প্রতিটি সারিকে s ধরে দেখা, ∃e-এর জন্য একটি “সাক্ষী” ভর্তি পাওয়া যায় কি না।

s → enrollments-এ সাক্ষীলাল = এই ধাপের সারি

            
মূল্যায়ন
s (বর্তমান সারি)
∃e-এর সাক্ষী
সূত্রের মান
ফলে কয়টি tuple
  1. প্রস্তুতি। enrollments-এর ১৩টি সারির মধ্যে CSE301-এর তিনটি: 2101, 2102 আর 2201-এর। প্রতিটি s-এর জন্য প্রশ্ন একটিই — শর্ত মানে এমন অন্তত একটি e আছে কি?
  2. s = Arif. প্রথম ভর্তি CSE201, শর্তের অর্ধেক মেলে না। দ্বিতীয়টি (2101, CSE301) — দুটি শর্তই মেলে। একটি সাক্ষী পেলেই ∃ সত্য, তাই আর খোঁজার দরকার নেই। t = ⟨Arif Hossain⟩.
  3. s = Bristy. একইভাবে (2102, CSE301) সাক্ষী। ফলে দ্বিতীয় tuple.
  4. s = Chayon. তাঁর একমাত্র ভর্তি EEE101. কোনো e দুটি শর্ত একসাথে মানে না, তাই ∃ মিথ্যা — বাদ।
  5. s = Dilruba. তৃতীয় ভর্তিটি CSE301, যদিও grade NULL. সূত্রে grade-এর কোনো উল্লেখ নেই, তাই এটি সাক্ষী হিসেবে বৈধ।
  6. বাকি চারজন। Emon, Fahim, Gulshan-এর ভর্তিতে CSE301 নেই; Habib-এর কোনো ভর্তিই নেই। সবার জন্য ∃ মিথ্যা।
  7. ফল। তিনটি tuple. Calculus-এর ফল একটি গাণিতিক সেট, তাই কোনো নাম দুবার আসে না। SQL কিন্তু multiset (bag) নিয়ে কাজ করে — পরের এডিটরে এর প্রভাব দেখবেন।
-- ∃e ∈ enrollments (...) সরাসরি EXISTS হয়ে গেল
SELECT s.name FROM students s
WHERE EXISTS (SELECT 1 FROM enrollments e
              WHERE e.student_id = s.student_id
                AND e.course_id = 'CSE301');

-- ∃e-কে বাইরে টেনে আনলে: FROM-এ দুটি tuple variable
SELECT DISTINCT s.name FROM students s, enrollments e
WHERE e.student_id = s.student_id AND e.course_id = 'CSE301';

দুটি কোয়েরিরই ফল একই তিনটি নাম: Arif Hossain, Bristy Saha, Dilruba Khatun. প্রথমটি TRC-র হুবহু অনুবাদ: ∃e হয়েছে EXISTS, আর ভেতরের s.student_id হলো free চলকের বাইরের রেফারেন্স — একটি correlated subquery. দ্বিতীয়টিতে ∃e বাইরে এসেছে: FROM students s, enrollments e মানে s আর e দুটি চলকই সব জোড়ার ওপর ঘোরে। একজন শিক্ষার্থীর CSE301-এর দুটি ভর্তি থাকলে (ভিন্ন term-এ) এই রূপে তাঁর নাম দুবার আসত, তাই DISTINCT. Calculus-এর সেট আর SQL-এর bag-এর পার্থক্য এখানেই।

৪ · ¬ আর ∀ — “কেউ নেই” আর “সবাই”

¬∃ দিয়ে অনেক প্রশ্ন লেখা যায়, যা প্রথম দেখায় aggregate-এর প্রশ্ন মনে হয়। যেমন “সর্বোচ্চ cgpa কার?” — MAX ছাড়াই: “এমন শিক্ষার্থী, যাঁর চেয়ে বেশি cgpa কারও নেই”।

{ t | t ∈ students ∧ ¬∃s ∈ students (s.cgpa > t.cgpa) }
-- হুবহু অনুবাদ: ¬∃ → NOT EXISTS
SELECT t.name, t.cgpa FROM students t
WHERE NOT EXISTS (SELECT 1 FROM students s WHERE s.cgpa > t.cgpa);

-- NULL-এর জন্য একটি শর্ত বাড়তি লাগে
SELECT t.name, t.cgpa FROM students t
WHERE t.cgpa IS NOT NULL
  AND NOT EXISTS (SELECT 1 FROM students s WHERE s.cgpa > t.cgpa);

প্রথম ফলে দুটি সারি: Dilruba Khatun | 3.9 আর Fahim Karim | NULL! Fahim-এর জন্য ভেতরের s.cgpa > NULL প্রতিটি সারিতে UNKNOWN, তাই ভেতরের ফল ফাঁকা, NOT EXISTS সত্য। Pure calculus-এ এমন হয় না, কারণ সেখানে প্রতিটি মান জানা। SQL-এর তিন-মানের যুক্তি (পর্ব ১৬) ¬∃-কে বদলে দিয়েছে। দ্বিতীয় কোয়েরি সঠিক: শুধু Dilruba Khatun | 3.9. শিক্ষাটি হলো, calculus থেকে SQL-এ অনুবাদ করার সময় প্রতিটি nullable কলাম আলাদা করে ভাবতে হয়।

∀ — “সব কোর্স নিয়েছেন”

এবার relational division-এর প্রশ্ন (পর্ব ৩৩-এর ÷): “CSE বিভাগের সব কোর্সে ভর্তি হয়েছেন এমন শিক্ষার্থী”। ∀ দিয়ে কথাটি প্রায় বাংলার মতোই লেখা যায়:

{ t | ∃s ∈ students ( t.name = s.name
      ∧ ∀c ∈ courses ( c.dept_id = 1 →
            ∃e ∈ enrollments ( e.student_id = s.student_id
                             ∧ e.course_id  = c.course_id ) ) ) }

“প্রতিটি কোর্স c-এর জন্য: c যদি CSE-র হয়, তবে s-এর এমন একটি ভর্তি আছে যার কোর্স c.” → কেন? ∀c ∈ courses সব সাতটি কোর্সের ওপর ঘোরে, EEE101 আর BBA101-সহ। ∧ লিখলে (c.dept_id = 1 ∧ …) EEE101-এর জন্য শর্তটি মিথ্যা হতো, আর ∀ ভেঙে যেত। → লিখলে EEE101-এর জন্য বাঁ পাশ মিথ্যা, তাই পুরো implication আপনা থেকেই সত্য — অপ্রাসঙ্গিক কোর্স কোনো বাধা দেয় না। একে বলে vacuous truth.

SQL-এ ∀ নেই, আছে শুধু EXISTS. তাই সূত্রটি ∃ আর ¬-এ বদলাতে হয়। কীভাবে, আর তারপর মূল্যায়ন কীভাবে হয়, ধাপে ধাপে দেখুন। উদাহরণটি একটু ছোট করা হয়েছে: ভাজক হলো Tasnim Chowdhury (instructor 6)-এর পড়ানো সব কোর্স — CSE301 আর CSE302.

সূত্র / মূল্যায়নলাল = এই ধাপের বদল

            
অবস্থা
ধাপের কাজ
প্রতি-উদাহরণ c
ফলে
  1. মূল সূত্র। P(c) = “c-এর শিক্ষক 6”, Q(c) = “s-এর একটি ভর্তি c-তে আছে”। আমরা চাই ∀c (P → Q). SQL-এ ∀ বা → কোনোটিই নেই।
  2. → সরানো। implication-এর সংজ্ঞা: P → Q মিথ্যা শুধু তখন, যখন P সত্য আর Q মিথ্যা। তাই P → Q ≡ ¬P ∨ Q.
  3. ∀ সরানো। “সব c-এর জন্য φ” ≡ “এমন কোনো c নেই, যার জন্য φ মিথ্যা”। প্রথম ¬ আর দ্বিতীয় ¬ দুটি NOT EXISTS-এর বীজ।
  4. De Morgan. ¬(¬P ∨ Q) = ¬¬P ∧ ¬Q = P ∧ ¬Q. চূড়ান্ত রূপ: ¬∃c (P(c) ∧ ¬Q(c)), আর ¬Q নিজেই ¬∃e (…). বাংলায়: “Tasnim পড়ান অথচ s নেয়নি — এমন কোর্স একটিও নেই।”
  5. Arif. Tasnim-এর দুটি কোর্সই Arif নিয়েছেন। বাকি পাঁচটি কোর্সে P মিথ্যা, তাই সেগুলো প্রতি-উদাহরণ হতে পারে না। কোনো প্রতি-উদাহরণ নেই, তাই ¬∃ সত্য।
  6. Bristy. CSE301 নিয়েছেন, CSE302 নেননি। CSE302-এর জন্য P সত্য আর ¬Q সত্য — একটি প্রতি-উদাহরণ। একটি পেলেই ¬∃ মিথ্যা।
  7. Dilruba. CSE301-এর ভর্তি আছে (grade NULL হলেও ভর্তি তো আছে), কিন্তু CSE302 নেই। বাদ।
  8. বাকি পাঁচজন। কারও CSE301-ই নেই, তাই প্রথম কোর্সেই প্রতি-উদাহরণ।
  9. ফল: Arif. সাবধান: ভাজক ফাঁকা হলে (যেমন instructor 99-এর কোনো কোর্স নেই) P সবসময় মিথ্যা, কোনো প্রতি-উদাহরণ নেই, তাই ৮ জনই ফলে আসেন। গাণিতিকভাবে এটিই সঠিক — “নেই এমন সব কোর্স নিয়েছেন” তুচ্ছভাবে সত্য।
-- ¬∃c ∈ courses (P(c) ∧ ¬∃e ∈ enrollments (...))
SELECT s.name FROM students s
WHERE NOT EXISTS (                    -- এমন কোনো কোর্স নেই…
  SELECT 1 FROM courses c
  WHERE c.instructor_id = 6           -- …যা Tasnim পড়ান
    AND NOT EXISTS (                  -- …অথচ s নেয়নি
      SELECT 1 FROM enrollments e
      WHERE e.student_id = s.student_id
        AND e.course_id = c.course_id));

ফল: Arif Hossain — ঠিক ডায়াগ্রামের মতো। দুই স্তরের NOT EXISTS সূত্রের দুটি ¬-এর সরাসরি প্রতিচ্ছবি। ভেতরের সবচেয়ে গভীর subquery দুটি বাইরের চলক ব্যবহার করে: s (সবচেয়ে বাইরের) আর c (মাঝের). নিজে পরীক্ষা করুন: 6-এর জায়গায় 99 লিখে চালালে ৮ জনই আসবেন — vacuous truth চোখের সামনে।

শুরুর প্রশ্নটি — CSE বিভাগের সব কোর্স — চালালে কী হয়? আর একই কাজ EXCEPT দিয়ে লেখা যায় কীভাবে: “ভাজকের কোর্সগুলো থেকে s-এর কোর্সগুলো বাদ দিলে কিছু বাকি থাকে না”?

-- CSE বিভাগের (dept_id = 1) সব কোর্স নিয়েছেন কে?
SELECT s.name FROM students s
WHERE NOT EXISTS (
  SELECT 1 FROM courses c
  WHERE c.dept_id = 1
    AND NOT EXISTS (SELECT 1 FROM enrollments e
                    WHERE e.student_id = s.student_id
                      AND e.course_id = c.course_id));

-- EXCEPT রূপ: (ভাজক − s-এর কোর্স) ফাঁকা কি?
SELECT s.name FROM students s
WHERE NOT EXISTS (
  SELECT course_id FROM courses WHERE instructor_id = 6
  EXCEPT
  SELECT course_id FROM enrollments WHERE student_id = s.student_id);

প্রথম ফল “(কোনো সারি নেই)”। CSE-র কোর্স চারটি: CSE101, CSE201, CSE301, CSE302. Arif-এর CSE101 নেই, Dilruba-র CSE302 নেই, Bristy-র CSE101 আর CSE302 দুটিই নেই — প্রত্যেকের অন্তত একটি প্রতি-উদাহরণ আছে। ফাঁকা ফলও একটি সঠিক উত্তর। দ্বিতীয় ফল আবার Arif Hossain. EXCEPT রূপটি পর্ব ৩৩-এর division-এর সংজ্ঞার কাছাকাছি: ভাজক ⊆ s-এর কোর্স ⇔ ভাজক − s-এর কোর্স = ∅. তৃতীয় একটি রূপও প্রচলিত — গুনে মেলানো: HAVING COUNT(DISTINCT e.course_id) = (SELECT COUNT(*) …). সেটি পড়তে সহজ, কিন্তু pure calculus-এর বাইরে (COUNT লাগে), আর ভাজক ফাঁকা হলে join-এর কারণে কেউই আসেন না — calculus-এর উত্তরের সাথে মেলে না।

৫ · Safety — যখন উত্তর অসীম

এবার একটি নির্দোষ-দেখতে প্রশ্ন: {t | ¬(t ∈ students)} — “যে tuple students-এ নেই”। এর উত্তর কী? ⟨1, 'Rahim', …⟩ নেই, ⟨2, 'Rahim', …⟩ নেই, ⟨3, 'Karim', …⟩ নেই … পৃথিবীর সব সম্ভাব্য সারি, শুধু ৮টি বাদে। উত্তর অসীম। কোনো কম্পিউটার এটি তালিকা করতে পারবে না। আরেকটি উদাহরণ: {t | t ∈ students ∨ t.cgpa > 3} — ∨-এর ডান পাশ students-এর বাইরের যেকোনো tuple-কে ঢুকতে দেয়, যার cgpa 3-এর বেশি। আবার অসীম।

NID ডেটাবেসের অপারেটরকে বললেন, “যাঁরা এই তালিকায় নেই, তাঁদের নাম দিন।” প্রশ্নটির উত্তর নেই — পৃথিবীর বাকি সব মানুষ, এমনকি যাঁরা এখনো জন্মাননি। কিন্তু “এই জেলার ভোটারদের মধ্যে যাঁরা গত নির্বাচনে ভোট দেননি” বললে উত্তর সসীম, কারণ “না”-টি একটি নির্দিষ্ট তালিকার ভেতরে সীমাবদ্ধ।

সমস্যার মূলে ¬ আর ∨: এরা চলককে কোনো টেবিলের ভেতরে আটকে রাখে না। সমাধানের জন্য দরকার expression-এর domain. সূত্র P-এর domain, dom(P), হলো সেই সব মানের সেট, যা P-এ ধ্রুবক হিসেবে আছে, অথবা P-এ উল্লেখ করা কোনো টেবিলের কোনো সারিতে আছে। যেমন {t | t ∈ students ∧ t.cgpa > 3.5}-এর domain হলো students-এর সব মান আর 3.5.

সংজ্ঞা — safe TRC expression

{t | P(t)} safe, যদি এর উত্তরের প্রতিটি tuple-এর প্রতিটি মান dom(P)-এর মধ্যে থাকে (Silberschatz–Korth–Sudarshan-এর সংজ্ঞা)। Safe হলে উত্তর সবসময় সসীম, আর উত্তর বের করতে টেবিলের বাইরের কোনো মান “কল্পনা” করতে হয় না। DRC-তে আরও দুটি শর্ত যোগ হয়: প্রতিটি ∃x (P1)-এর সত্যতা dom(P1)-এর মান দেখেই ঠিক করা যায়, আর প্রতিটি ∀x (P1)-ও শুধু dom(P1) দেখে।

সহজ ব্যবহারিক নিয়ম: প্রতিটি free চলককে কোনো ধনাত্মক ∈ R atom দিয়ে একটি টেবিলে বেঁধে দিন, আর ¬ ব্যবহার করুন শুধু সেই বাঁধা চলকের ওপর। {t | ¬(t ∈ students)} unsafe, কিন্তু {t | t ∈ students ∧ ¬∃e ∈ enrollments (e.student_id = t.student_id)} safe — “না”-টি students-এর ভেতরে সীমিত। তাত্ত্বিক বইয়ে এই ধরনের নিয়মকে বলা হয় range-restricted বা safe-range সূত্র।

-- safe ¬: কোনো ভর্তি নেই এমন শিক্ষার্থী
SELECT t.student_id, t.name FROM students t
WHERE NOT EXISTS (SELECT 1 FROM enrollments e
                  WHERE e.student_id = t.student_id);

-- একই প্রশ্ন, algebra-র − (EXCEPT) দিয়ে
SELECT student_id FROM students
EXCEPT
SELECT student_id FROM enrollments;

-- কোনো শিক্ষার্থী নেই এমন বিভাগ
SELECT d.name FROM departments d
WHERE NOT EXISTS (SELECT 1 FROM students s WHERE s.dept_id = d.dept_id);

প্রথম ফল: 2302 | Habib Ullah. দ্বিতীয় ফল: 2302. তৃতীয় ফল: English. তিনটিই safe, কারণ প্রতিটি কোয়েরিতে ফলের চলক FROM-এর একটি টেবিলে বাঁধা। খেয়াল করুন, SQL-এ unsafe প্রশ্ন লেখাই প্রায় অসম্ভব: SELECT … WHERE NOT … লিখতে হলে আগে FROM লাগে, আর FROM চলককে একটি টেবিলের সারিতে আটকে দেয়। SQL-এর ডিজাইন safety-কে ব্যাকরণেই ঢুকিয়ে দিয়েছে।

৬ · Domain relational calculus আর QBE

Domain relational calculus (DRC)-এ চলক পুরো সারি নয়, একেকটি কলামের মান — domain variable. নামের “domain” শব্দটি এসেছে পর্ব ৫-এর domain থেকে — চলকটি একটি কলামের domain-এর মান নেয়। প্রশ্নের আকার:

{ ⟨x1, x2, …, xn⟩ | P(x1, x2, …, xn) }

Atom-ও বদলায়: ⟨x1, …, xn⟩ ∈ R (এই মানগুলো এই ক্রমে R-এর একটি সারি), x θ y আর x θ c. চলকের অবস্থানই কলাম চিনিয়ে দেয়, তাই টেবিলের কলামের ক্রম মনে রাখতে হয়: students(student_id, name, dept_id, batch, cgpa), enrollments(student_id, course_id, term, grade). আগের তিনটি প্রশ্ন DRC-তে:

-- cgpa > 3.5 এমন শিক্ষার্থীর নাম
{ ⟨n⟩ | ∃i, d, b, g ( ⟨i, n, d, b, g⟩ ∈ students ∧ g > 3.5 ) }

-- CSE301-এর শিক্ষার্থীর নাম: একই i দুই জায়গায় = join
{ ⟨n⟩ | ∃i, d, b, g ( ⟨i, n, d, b, g⟩ ∈ students
        ∧ ∃tm, gr ( ⟨i, 'CSE301', tm, gr⟩ ∈ enrollments ) ) }

-- CSE বিভাগের সব কোর্স নিয়েছেন (∀)
{ ⟨n⟩ | ∃i, d, b, g ( ⟨i, n, d, b, g⟩ ∈ students
        ∧ ∀c, ti, cr, ins ( ⟨c, ti, 1, cr, ins⟩ ∈ courses
              → ∃tm, gr ( ⟨i, c, tm, gr⟩ ∈ enrollments ) ) ) }

দুটি কৌশল লক্ষ করুন। (১) একই চলক দুই জায়গায় বসালে সেটিই join শর্ত: students-এর i আর enrollments-এর i একই মান হতে বাধ্য, আলাদা করে = লিখতে হয় না। (২) ধ্রুবক সরাসরি অবস্থানে বসে: 'CSE301' enrollments-এর দ্বিতীয় ঘরে, 1 courses-এর তৃতীয় (dept_id) ঘরে। নিচের SQL-এ DRC-র এই দুই কৌশলের প্রতিফলন দেখুন — একটি সাধারণ join আর একটি self join:

-- {⟨ti, n⟩ | ∃c, cr, i (⟨c, ti, 1, cr, i⟩ ∈ courses
--            ∧ ∃d, s, m (⟨i, n, d, s, m⟩ ∈ instructors))}
SELECT c.title, i.name AS teacher
FROM courses c, instructors i
WHERE c.instructor_id = i.instructor_id AND c.dept_id = 1;

-- {⟨n, mn⟩ | ∃i, d, s, m, s2, m2 (⟨i, n, d, s, m⟩ ∈ instructors
--            ∧ ⟨m, mn, d, s2, m2⟩ ∈ instructors)}
SELECT i.name, m.name AS mentor
FROM instructors i, instructors m
WHERE i.mentor_id = m.instructor_id AND i.dept_id = m.dept_id;

প্রথম ফলে চারটি সারি: Structured Programming | Shahana Parvin, Data Structures | Dr. Anisur Rahman, Database Systems | Tasnim Chowdhury, Database Systems Lab | Tasnim Chowdhury. DRC-র শেয়ার করা i হলো SQL-এর c.instructor_id = i.instructor_id, আর অবস্থানে বসা 1 হলো c.dept_id = 1. দ্বিতীয় ফলে তিনটি সারি: Shahana Parvin | Dr. Anisur Rahman, Rumana Islam | Dr. Mahbub Alam, Tasnim Chowdhury | Shahana Parvin. DRC-তে m প্রথম সারির শেষ ঘরে (mentor_id) আর দ্বিতীয় সারির প্রথম ঘরে (instructor_id) বসেছে, আর d দুই সারিতেই — “একই বিভাগের mentor” শর্তটি কোনো = চিহ্ন ছাড়াই লেখা হয়ে গেছে।

Query-By-Example — ছবিতে আঁকা DRC

১৯৭০-এর দশকের মাঝামাঝি IBM-এর Moshé M. Zloof একটি ভিন্ন ধারণা নিয়ে আসেন: ব্যবহারকারী সূত্র লিখবেন না, টেবিলের ফাঁকা কাঠামোয় উদাহরণ বসাবেন। ভাষাটির নাম Query-By-Example (QBE); প্রথম প্রকাশিত বিবরণ ১৯৭৫ সালের। QBE আসলে DRC-র দৃশ্যরূপ। কোনো ঘরে আন্ডারস্কোরওয়ালা “উদাহরণ” (example element, যেমন _S) একটি domain variable; সাধারণ মান (যেমন CSE301) একটি ধ্রুবক; P. মানে “এটি দেখানো” (print); আর দুই টেবিলে একই উদাহরণ বসালে সেটি join — ঠিক DRC-র শেয়ার করা চলকের মতো। “CSE301-এর শিক্ষার্থীর নাম” QBE-তে:

studentsstudent_idnamedept_idbatchcgpa
_SP._N
enrollmentsstudent_idcourse_idtermgrade
_SCSE301

দুই টেবিলে একই _S মানে “একই শিক্ষার্থী”; name-এর ঘরে P._N মানে নামটি দেখানো। cgpa-র ঘরে >3.5 লিখলে সেটি শর্ত। কোনো সারির বাঁ দিকের ঘরে ¬ বসালে সেই সারি “থাকা চলবে না” (¬∃). আজ এই ধারণা সবচেয়ে পরিচিত Microsoft Access-এর query design grid-এ: টেবিল টেনে এনে কলাম বাছাই, Criteria সারিতে >3.5, আর দুটি টেবিলের কলামে লাইন টেনে join. ধাঁচটি QBE থেকে এসেছে, যদিও পর্দার পেছনে Access শেষে SQL-ই তৈরি করে। সহজ প্রশ্নে এই পদ্ধতি দারুণ; কিন্তু ∀ বা দুই স্তরের ¬-ওয়ালা প্রশ্ন ছবিতে আঁকা বেশ ঝামেলার।

৭ · Codd-এর উপপাদ্য — তিন ভাষা, একই শক্তি

Codd-এর উপপাদ্য (১৯৭২)

Relational algebra (σ, π, ×, ∪, −, ρ), safe tuple relational calculus আর safe domain relational calculus — এই তিনটির প্রকাশক্ষমতা সমান। অর্থাৎ যেকোনো একটিতে লেখা প্রতিটি প্রশ্ন বাকি দুটিতেও লেখা যায়, আর প্রতিটি ডেটাবেসে একই উত্তর দেয়।

Codd-এর উপপাদ্যটি তিনি দেন ১৯৭২ সালের “Relational Completeness of Data Base Sublanguages” প্রবন্ধে; calculus থেকে algebra-য় যাওয়ার তাঁর পদ্ধতিটি “Codd's reduction algorithm” নামে পরিচিত। প্রমাণের ধারণা তিন টুকরোয়, প্রতিটি সূত্রের গঠনের ওপর induction:

(ক) Algebra → DRC. প্রতিটি algebra expression E-এর জন্য একটি সূত্র φE বানাই, যার free চলক E-এর কলামগুলো। টেবিল R → ⟨x1, …, xn⟩ ∈ R. σA θ c(E) → φE ∧ xA θ c. π (কলাম বাদ) → বাদ পড়া চলকের ওপর ∃. E1 × E2 → আলাদা চলক নিয়ে φE1 ∧ φE2. E1 ∪ E2 → φE1 ∨ φE2. E1 − E2 → φE1 ∧ ¬φE2. প্রতিটি ধাপে প্রতিটি চলক কোনো টেবিল থেকে আসে, তাই ফল safe.

(খ) Safe DRC → algebra. প্রথমে একটি algebra expression D বানাই, যা সূত্রের domain: উল্লেখ করা প্রতিটি টেবিলের প্রতিটি কলামের π, আর সূত্রের ধ্রুবকগুলো, সব ∪ করে। এবার সূত্রের গঠন ধরে: atom ⟨x̄⟩ ∈ R → R; x θ y → σx θ y(D × D); ∧ → natural join; ∨ → দুই পাশের চলক মেলাতে বাড়তি চলকের জন্য × D, তারপর ∪; ¬φ → Dk − Eφ; ∃x φ → x বাদ দিয়ে π; ∀ → ¬∃¬. ¬-কে Dk-এর ভেতরে সীমিত রাখা নিরাপদ, কারণ সূত্রটি safe — এর উত্তরের কোনো মান D-এর বাইরে থাকতে পারে না। ঠিক এখানেই safety-র শর্ত প্রমাণে কাজে লাগে।

(গ) TRC ↔ DRC. R-এর (n কলাম) ওপর একটি tuple variable t-কে n-টি domain variable t1, …, tn দিয়ে বদলান, আর t.A-কে সংশ্লিষ্ট tA দিয়ে। উল্টো দিকেও একইভাবে। এভাবে তিনটি ভাষা একটি বৃত্তে যুক্ত হয়।

SQL-এর জন্য এর মানে কী? কোনো ভাষা relational algebra-র প্রতিটি প্রশ্ন প্রকাশ করতে পারলে Codd তাকে বলেন relationally complete. SQL তা পারে, আর তার চেয়েও বেশি পারে (aggregate, ORDER BY, recursion — অংশ ৯). আরও গুরুত্বপূর্ণ হলো উল্টো ব্যবহার: SQL-এর calculus-ঘেঁষা প্রশ্নকে DBMS algebra-র একটি গাছে (query plan) বদলাতে পারে, আর সেই গাছকে নিরাপদে সাজিয়ে দ্রুততর করতে পারে (পর্ব ৪২–৪৩). উপপাদ্যটি নিশ্চয়তা দেয় যে এই অনুবাদ সবসময় সম্ভব। Declarative ভাষার পেছনে একটি optimizer থাকতে পারে, তার তাত্ত্বিক ভিত্তি এটিই।

অনুবাদের টেবিল — algebra, TRC আর SQL

প্রশ্নRelational algebraTRCSQL
cgpa > 3.5σcgpa>3.5(students){t | t ∈ students ∧ t.cgpa > 3.5}SELECT * FROM students WHERE cgpa > 3.5
তাঁদের নামπname(σcgpa>3.5(students)){t | ∃s ∈ students (s.cgpa > 3.5 ∧ t.name = s.name)}SELECT name … WHERE cgpa > 3.5
CSE301-এর শিক্ষার্থীর নামπname(students ⋈ σcourse_id='CSE301'(enrollments)){t | ∃s ∈ students (t.name = s.name ∧ ∃e ∈ enrollments (e.student_id = s.student_id ∧ e.course_id = 'CSE301'))}WHERE EXISTS (…) অথবা join + DISTINCT
কোনো ভর্তি নেইπstudent_id(students) − πstudent_id(enrollments){t | ∃s ∈ students (t.student_id = s.student_id ∧ ¬∃e ∈ enrollments (e.student_id = s.student_id))}NOT EXISTS অথবা EXCEPT
2101 আর 2102 দুজনেই নিয়েছেন এমন কোর্সπcourse_id(σstudent_id=2101(enrollments)) ∩ πcourse_id(σstudent_id=2102(enrollments)){t | ∃u ∈ enrollments (u.student_id = 2101 ∧ t.course_id = u.course_id) ∧ ∃v ∈ enrollments (v.student_id = 2102 ∧ t.course_id = v.course_id)}INTERSECT অথবা EXISTS
শিক্ষক আর তাঁর mentorπi.name, m.name(ρi(instructors) ⋈i.mentor_id = m.instructor_id ρm(instructors)){t | ∃i ∈ instructors ∃m ∈ instructors (i.mentor_id = m.instructor_id ∧ t.name = i.name ∧ t.mentor = m.name)}FROM instructors i, instructors m
সর্বোচ্চ cgpastudents − πstudents.*(students ⋈cgpa < s2.cgpa ρs2(students)){t | t ∈ students ∧ ¬∃s ∈ students (s.cgpa > t.cgpa)}NOT EXISTS + cgpa IS NOT NULL
Tasnim-এর সব কোর্স নিয়েছেনπstudent_id, course_id(enrollments) ÷ πcourse_id(σinstructor_id=6(courses)){t | ∃s ∈ students (t.student_id = s.student_id ∧ ∀c ∈ courses (c.instructor_id = 6 → ∃e ∈ enrollments (…)))}দুই স্তরের NOT EXISTS

পঞ্চম সারির ∩ একটি উদাহরণ যেখানে calculus-এ ∧ আর algebra-য় ∩ একই কাজ করে। নিজে চালিয়ে মিলিয়ে নিন:

-- algebra-র ∩
SELECT t.course_id FROM enrollments t WHERE t.student_id = 2101
INTERSECT
SELECT u.course_id FROM enrollments u WHERE u.student_id = 2102;

-- TRC-র ∧ ∃v: “2101-এর কোর্স, যা 2102-এরও আছে”
SELECT DISTINCT t.course_id FROM enrollments t
WHERE t.student_id = 2101
  AND EXISTS (SELECT 1 FROM enrollments u
              WHERE u.student_id = 2102 AND u.course_id = t.course_id);

দুটির ফলই CSE201 আর CSE301. Arif-এর কোর্স CSE201, CSE301, CSE302; Bristy-র CSE201, CSE301 — মিল দুটি। INTERSECT নিজেই duplicate বাদ দেয় (সেট-অপারেশন), কিন্তু EXISTS রূপে DISTINCT লেখা হয়েছে, কারণ Arif একই কোর্স দুই term-এ নিলে t-এর দুটি সারি মিলত। আবারও সেট বনাম bag.

১৯৭০ সালে Codd relational model প্রস্তাব করেন। ১৯৭১ সালে তিনি tuple calculus-ভিত্তিক একটি ভাষা প্রস্তাব করেন — Data Sublanguage ALPHA; এটি কখনো পূর্ণ পণ্য হয়নি, কিন্তু দেখিয়ে দেয় যে ডেটা চাওয়া যায় যুক্তির ভাষায়। ১৯৭২ সালে তিনি algebra আর calculus-এর সমতা প্রমাণ করেন। এরপর দুটি গবেষণা দল দুই পথে হাঁটে। IBM-এর System R দল (Chamberlin, Boyce প্রমুখ) ইংরেজি শব্দের SEQUEL বানায়, যা পরে SQL হয়। Berkeley-তে Michael Stonebraker ও Eugene Wong-এর Ingres প্রকল্প বানায় QUEL, যা tuple calculus-এর আরও কাছাকাছি। পরে বাজার SQL-কে বেছে নেয়, আর ১৯৮৬ সালে ANSI SQL-কে স্ট্যান্ডার্ড করে।

৮ · SQL-এর শিকড়ে TRC — FROM, WHERE আর EXISTS

QUEL-এ tuple calculus প্রায় খালি চোখেই দেখা যায়। “cgpa 3.5-এর বেশি শিক্ষার্থীর নাম”:

range of s is students
retrieve (s.name) where s.cgpa > 3.5

range of s is students মানে “s একটি tuple variable, যা students-এর ওপর ঘোরে” — হুবহু s ∈ students. SQL একই জিনিস লেখে FROM students s দিয়ে। পুরো মিলটি এরকম: FROM প্রতিটি tuple variable-কে একটি টেবিলে বাঁধে (∈ atom, আর বাঁধা চলকগুলো ∃-এর মতো); WHERE হলো সূত্র P; SELECT তালিকা ঠিক করে ফলের tuple t-এর কলাম (t.name = s.name); EXISTS হলো ∃, NOT EXISTS হলো ¬∃; AND, OR, NOT হলো ∧, ∨, ¬. একটি TRC সূত্র থেকে SQL লেখার পুরো পথটি ধাপে ধাপে দেখুন:

TRC → SQLলাল = এই ধাপে অনুবাদ হওয়া অংশ

            
মিলের তালিকা
TRC অংশ
SQL অংশ
  1. শুরু। TRC সূত্রটি “CSE301-এর শিক্ষার্থীর নাম”। অনুবাদ চলবে বাইরে থেকে ভেতরে, প্রতিটি টুকরোর একটি SQL প্রতিরূপ আছে।
  2. ফলের আকার। t-এর একমাত্র কলাম name, আর তা আসে s থেকে। তাই SELECT তালিকা: s.name. t-এর জন্য SQL-এ আলাদা চলক লাগে না — SELECT তালিকাই t.
  3. ∃s ∈ students. বাইরের quantifier-এর চলক FROM-এ যায়: FROM students s. SQL-এ FROM-এর চলকগুলো অদৃশ্য ∃-এর মতো আচরণ করে।
  4. ∃e ∈ enrollments. ভেতরের ∃ হয় EXISTS (SELECT 1 FROM enrollments e WHERE …). e চলকটি subquery-র FROM-এ বাঁধা — e bound, s এখানে বাইরের রেফারেন্স।
  5. Atom. ∧ হয় AND, আর তুলনাগুলো প্রায় হুবহু। অনুবাদ শেষ — আগের এডিটরের প্রথম কোয়েরিটিই এটি।
  6. বিকল্প রূপ। ∃s (A ∧ ∃e B) ≡ ∃s ∃e (A ∧ B), কারণ A-তে e নেই। তাই e-কেও বাইরের FROM-এ আনা যায়। কিন্তু SQL-এর FROM-এর ফল একটি bag, তাই সেটের অর্থ ফিরে পেতে DISTINCT লাগে।

SQL-এ ∀ কেন অস্বস্তিকর

SQL-এর মূল ব্যাকরণে ∀-এর সরাসরি কোনো প্রতিরূপ নেই; FOR ALL বলে কোনো শব্দ নেই। তাই প্রতিটি “সব”-প্রশ্নকে মাথার ভেতরে অংশ ৪-এর মতো ¬∃…¬-এ বদলাতে হয়, তারপর দুই স্তরের NOT EXISTS লিখতে হয়। পড়ার সময় উল্টো কাজ: দুটি NOT দেখে “সব”-এ ফিরে যাওয়া। এজন্য division-এর প্রশ্ন বিশ্ববিদ্যালয়ের পরীক্ষা আর ইন্টারভিউতে এত জনপ্রিয় — এটি দেখায় আপনি যুক্তিটি বুঝেছেন কি না। আরেকটি সূক্ষ্মতা পর্ব ২১ থেকে মনে রাখুন: ¬∃-এর জায়গায় NOT IN লিখলে nullable কলামে ফল নীরবে ফাঁকা হয়ে যেতে পারে। Calculus-এর ¬∃-এর সবচেয়ে বিশ্বস্ত অনুবাদ NOT EXISTS.

৯ · Relational completeness-এর সীমা — recursion আর aggregate

Codd-এর উপপাদ্য বলে তিনটি ভাষা সমান — কিন্তু সমান মানে সর্বশক্তিমান নয়। Pure relational calculus দুটি পরিচিত কাজ পারে না।

১. Recursion / transitive closure. “Dr. Anisur Rahman-এর অধীনে প্রত্যক্ষ বা পরোক্ষভাবে কারা আছেন?” — Shahana-র mentor তিনি, আর Tasnim-এর mentor Shahana. এক ধাপের প্রশ্ন calculus-এ সহজ, দুই ধাপের প্রশ্নও (দুটি চলক জুড়ে)। কিন্তু “যত ধাপই হোক” লিখতে হলে ধাপের সংখ্যা জানা থাকতে হবে, কারণ প্রতিটি সূত্রে চলকের সংখ্যা নির্দিষ্ট। ১৯৭৯ সালে Alfred Aho আর Jeffrey Ullman প্রমাণ করেন, transitive closure relational algebra-য় (তাই calculus-এও) প্রকাশ করা যায় না। SQL এই সীমা পার হয়েছে SQL:1999-এর recursive CTE দিয়ে, যা পর্ব ২৫-এ দেখেছেন।

-- এক ধাপ: calculus-এ লেখা যায়
SELECT i2.name FROM instructors i1, instructors i2
WHERE i1.instructor_id = 1 AND i2.mentor_id = i1.instructor_id;

-- যেকোনো ধাপ: calculus-এর বাইরে, recursive CTE লাগে
WITH RECURSIVE under(id, name, depth) AS (
  SELECT instructor_id, name, 1 FROM instructors WHERE mentor_id = 1
  UNION ALL
  SELECT i.instructor_id, i.name, u.depth + 1
  FROM instructors i JOIN under u ON i.mentor_id = u.id)
SELECT * FROM under;

প্রথম ফল শুধু Shahana Parvin — Tasnim বাদ, কারণ তিনি দুই ধাপ নিচে। দ্বিতীয় ফল: 2 | Shahana Parvin | 1 আর 6 | Tasnim Chowdhury | 2. Recursive CTE নিজের ফলকে আবার নিজের ইনপুট বানায়, যতক্ষণ না নতুন সারি আসা থামে (fixpoint). এমন “নিজেকে ডাকা” কোনো calculus সূত্রে সম্ভব নয়। এখানে আরেকটি বাড়তি জিনিসও আছে: u.depth + 1-এর মতো হিসাব, যা pure calculus-এ নেই — সেখানে নতুন মান তৈরি হয় না।

২. Aggregate. COUNT, SUM, AVG বা “প্রতি বিভাগে কতজন” — pure calculus-এ এর কোনোটিই নেই। কারণ calculus-এর উত্তরের প্রতিটি মান dom(P) থেকে আসে, আর “৪ জন” সংখ্যাটি হয়তো কোনো টেবিলেই নেই। তাই SQL-এর GROUP BY (পর্ব ১৮) relational completeness-এর বাইরের একটি বাড়তি ক্ষমতা। পাঠ্যবইয়ে algebra-য় একে আলাদা অপারেটর হিসেবে যোগ করা হয় (𝒢 বা γ). Calculus-এ “সর্বোচ্চ” লেখা গেছে (অংশ ৪-এর ¬∃), কারণ সেটি একটি বিদ্যমান মান বেছে নেওয়া, নতুন মান বানানো নয়। কিন্তু “গড়” লেখা যায় না।

আরও গভীরে: তাত্ত্বিক কম্পিউটার বিজ্ঞানে relational calculus হলো first-order logic, আর “ডেটাবেসের ওপর FO” ঠিক কী পারে, তা নিয়ে আছে finite model theory. Recursion যোগ করলে পাওয়া যায় Datalog বা FO + least fixpoint. Neil Immerman আর Moshe Vardi ১৯৮০-এর দশকের শুরুতে আলাদাভাবে প্রমাণ করেন, ক্রমযুক্ত (ordered) ডেটাবেসে FO + least fixpoint ঠিক polynomial সময়ে উত্তর দেওয়া যায় এমন প্রশ্নগুলোই প্রকাশ করে। আরেকটি গভীর ফল: কোনো সূত্র domain-independent (অর্থাৎ safe) কি না, তা সাধারণভাবে কোনো অ্যালগরিদম দিয়ে ঠিক করা যায় না (undecidable) — তাই বাস্তবে safe-range-এর মতো ব্যাকরণগত নিয়ম ব্যবহার করা হয়।

১০ · AI-এর যুগে relational calculus

AI-কে “এই প্রশ্নটি TRC-তে লেখো” বললে সে সুন্দর চিহ্নসহ একটি সূত্র দেয়, যা দেখতে নির্ভুল। কিন্তু চিহ্নের ভুল চোখে পড়ে কম, আর একটি ভুল ∧ বা → পুরো অর্থ বদলে দেয়। আর AI-এর লেখা SQL চলে ঠিকই, কিন্তু NULL, duplicate বা ফাঁকা ভাজকে ভুল ফল দিতে পারে। বারবার দেখা যায় এমন ভুল:

AI-এর ভুলফলকীভাবে ধরবেন
∀c ∈ courses (c.dept_id = 1 ∧ …) — → এর বদলে ∧অন্য বিভাগের কোর্স থাকলেই সূত্র মিথ্যা, ফল ফাঁকা∀-এর সাথে →, ∃-এর সাথে ∧ — নিয়মটি মিলিয়ে নিন
বাঁ পাশে নেই এমন free চলকঅর্থহীন queryপ্রতিটি চলকের quantifier খুঁজুন
unsafe সূত্র (¬(t ∈ R) একা)অসীম উত্তরপ্রতিটি free চলক একটি ধনাত্মক ∈ R-এ বাঁধা কি না দেখুন
¬∃-এর জায়গায় NOT INnullable কলামে নীরবে ফাঁকা ফলNOT EXISTS দিয়ে একই কোয়েরি চালিয়ে সারি-সংখ্যা মেলান
∃-কে join বানিয়ে DISTINCT ভুলে যাওয়াduplicate নামEXISTS রূপ আর join রূপের সারি-সংখ্যা মেলান
“সব” প্রশ্নে COUNT মেলানো, DISTINCT ছাড়াএকই কোর্স দুবার নিলে ভুল; ফাঁকা ভাজকে ভিন্ন ফলফাঁকা ভাজক আর duplicate ভর্তি দিয়ে পরীক্ষা করুন
নেই এমন কলাম (students.course_id)ত্রুটি, বা ভুল টেবিলের কলামschema পাশে রেখে প্রতিটি নাম মিলিয়ে নিন

AI-কে calculus আর SQL একসাথে চাইলে schema, NULL-এর তথ্য আর যাচাইয়ের উপায় চেয়ে নিন:

আমার schema: students(student_id, name, dept_id, batch, cgpa — cgpa NULL হতে পারে), courses(course_id, title, dept_id, credits, instructor_id), enrollments(student_id, course_id, term, grade). প্রশ্ন: যেসব শিক্ষার্থী instructor 6-এর পড়ানো সব কোর্সে ভর্তি হয়েছে। ১. প্রথমে tuple relational calculus-এ লেখো, প্রতিটি চলক free না bound বলো, আর সূত্রটি safe কেন ব্যাখ্যা করো। ২. ∀-কে ধাপে ধাপে ¬∃…¬-এ বদলাও, প্রতিটি ধাপে কোন নিয়ম (implication, De Morgan) লাগল লেখো। ৩. তারপর SQLite-এ দুই স্তরের NOT EXISTS দিয়ে SQL লেখো; শুধু আমার দেওয়া কলাম ব্যবহার করো। ৪. instructor 6-এর কোনো কোর্স না থাকলে ফল কী হবে, আর COUNT দিয়ে লেখা সমাধানের সাথে তখন পার্থক্য কী, বলো। ৫. ৩টি ছোট পরীক্ষার ডেটা দাও (একজন সব নিয়েছে, একজন একটি বাদ, একজনের কোনো ভর্তি নেই), প্রত্যাশিত ফলসহ।

AI-এর লেখা calculus বা SQL হাতে পেলে — যাচাইয়ের চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • Relational algebra আর relational calculus-এর পার্থক্য? — Algebra procedural (অপারেশনের ক্রম বলে), calculus declarative (ফলের শর্ত বলে); Codd-এর উপপাদ্য অনুযায়ী safe calculus আর algebra সমান শক্তিশালী।
  • TRC আর DRC-র পার্থক্য? — TRC-র চলক পুরো সারি (t.A), DRC-র চলক একেকটি কলামের মান (⟨x1, …, xn⟩); QBE হলো DRC-ভিত্তিক।
  • Unsafe expression-এর উদাহরণ দিন। — {t | ¬(t ∈ R)}; উত্তর অসীম, কারণ t কোনো টেবিলে বাঁধা নয়।
  • SQL-এ “সব” (division) কীভাবে লিখবেন? — ∀x P ≡ ¬∃x ¬P, তাই দুই স্তরের NOT EXISTS: “এমন কোনো কোর্স নেই, যা সে নেয়নি”।
  • Relationally complete মানে কী, SQL কি তা? — Relational algebra-র প্রতিটি প্রশ্ন প্রকাশ করতে পারা; SQL তা পারে, আর aggregate ও recursion দিয়ে তার চেয়েও বেশি পারে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১Codd নিজে ১৯৭২ সালে প্রমাণ করলেন algebra আর calculus সমান। তাহলে বাস্তব ভাষা (SQL) কেন calculus-এর দিকে ঝুঁকল, algebra-র দিকে নয়? এতে optimizer-এর কী লাভ হলো?

Codd-এর নিজের পছন্দ ছিল calculus. ১৯৭১ সালের ALPHA ভাষা tuple calculus-ভিত্তিক, আর ১৯৭২ সালের প্রবন্ধে তিনি যুক্তি দেন যে একটি ডেটা ভাষার শক্তি মাপার মানদণ্ড হওয়া উচিত calculus — algebra সেখানে ছিল মূলত বাস্তবায়নের হাতিয়ার, calculus থেকে অনুবাদ করে চালানোর জন্য। তাঁর reduction algorithm ঠিক এই অনুবাদটিই করে।

১৯৭০-এর দশকের দুই বড় গবেষণা প্রকল্প এই পথ ধরে। IBM-এর System R দল চেয়েছিল এমন ভাষা, যা প্রোগ্রামার নন এমন মানুষও পড়তে পারবেন। গাণিতিক চিহ্নের বদলে ইংরেজি শব্দ — SELECT, FROM, WHERE — আর nested block. গঠনে এটি calculus-এর কাছাকাছি: চলক বাঁধা, শর্ত লেখা, কী চাই বলা। Berkeley-র Ingres-এর QUEL আরও সরাসরি tuple calculus. ১৯৭৪ সালে ACM-এর একটি কর্মশালায় (SIGFIDET, পরে যার নাম হয় SIGMOD) Codd আর network model-এর প্রবক্তা Charles Bachman-এর মধ্যে বিখ্যাত বিতর্ক হয়। সেখানে relational পক্ষের একটি মূল দাবি ছিল: ব্যবহারকারী বলবেন “কী চাই”, “কীভাবে” নয়।

Optimizer-এর লাভ এখানেই। Algebra-য় লেখা প্রশ্ন একটি নির্দিষ্ট অপারেশন-ক্রম বেঁধে দেয়। System R দলের Patricia Selinger ও সহকর্মীরা ১৯৭৯ সালে cost-based optimization-এর যে প্রবন্ধ প্রকাশ করেন, তার মূল ধারণা হলো: declarative প্রশ্ন থেকে অনেক সম্ভাব্য plan বানানো, statistics দেখে খরচ হিসাব করা, সবচেয়ে সস্তাটি বেছে নেওয়া। ব্যবহারকারী যদি ক্রম বলে দিতেন, optimizer-কে আগে প্রমাণ করতে হতো ক্রম বদলালে অর্থ বদলায় না — কাজটি সম্ভব, কিন্তু declarative রূপে সেই স্বাধীনতা গোড়া থেকেই থাকে।

মজার ব্যাপার, ভেতরে সব DBMS শেষ পর্যন্ত algebra-তেই ফেরে। PostgreSQL, MySQL বা SQL Server SQL পার্স করে একটি algebra-ধাঁচের গাছ বানায় — scan, filter, join, aggregate — আর সেই গাছকেই সাজায়। তাই দুটি ভাষারই নিজস্ব জায়গা আছে: calculus মানুষের সাথে কথা বলার, algebra যন্ত্রের ভেতরে কাজ করার।

মূল উপলব্ধি: Codd-এর উপপাদ্য দুই জগতের মধ্যে সেতু। ব্যবহারকারী লেখেন calculus-এর ভাষায়, DBMS চালায় algebra-র ভাষায়, আর উপপাদ্যটি নিশ্চয়তা দেয় যে অনুবাদে কিছু হারায় না।

প্র ০২Safety-র নিয়ম শুনে মনে হয় DBMS প্রতিটি সূত্র দেখে বলে দিতে পারত “এটি safe, ওটি নয়”। তাহলে বইগুলো কেন আলাদা ব্যাকরণগত নিয়ম (safe-range) দেয়, আর SQL কীভাবে সমস্যাটি এড়িয়ে যায়?

Safety-র আসল সংজ্ঞাটি “অর্থের” ওপর: সূত্রের উত্তর এমন হতে হবে যা শুধু ডেটাবেসের মান আর সূত্রের ধ্রুবক দিয়েই গড়া, আর domain বদলালেও উত্তর বদলায় না। তত্ত্বের ভাষায় একে বলে domain independence. সমস্যা হলো, যেকোনো first-order সূত্র domain-independent কি না, তা ঠিক করার কোনো সাধারণ অ্যালগরিদম নেই — প্রশ্নটি undecidable. এর মূল যোগসূত্র first-order logic-এর বিখ্যাত সীমার সাথে: কোনো সূত্র সব সসীম মডেলে সত্য কি না, তা ঠিক করা যায় না (Trakhtenbrot-এর উপপাদ্য, ১৯৫০). Abiteboul, Hull আর Vianu-র “Foundations of Databases” বইয়ে এই ফলগুলো বিস্তারিত আছে।

তাই বাস্তবে একটি ব্যাকরণগত শর্ত ব্যবহার হয়, যা যাচাই করা সহজ, আর যা মানলে safety নিশ্চিত। Safe-range বা range-restricted সূত্রের মূল কথা: প্রতিটি free চলককে একটি ধনাত্মক atom (x ∈ R বা x = c) দিয়ে বাঁধতে হবে; ∨-এর দুই পাশে একই free চলক থাকতে হবে; ¬ বসবে শুধু এমন সূত্রের ওপর, যার চলক আগেই অন্য কোথাও বাঁধা। শর্তটি কিছুটা কড়া — কিছু safe সূত্রও এতে আটকে যায় — কিন্তু প্রমাণ করা হয়েছে যে প্রতিটি domain-independent প্রশ্নের একটি safe-range রূপ আছে। ফলে কোনো প্রকাশক্ষমতা হারায় না।

SQL একই কাজ করেছে আরও সরল ভাবে। প্রতিটি চলককে (alias) FROM-এ একটি টেবিলের সাথে ঘোষণা করতে হয়। “t এমন সারি যা কোথাও নেই” লেখার ব্যাকরণই নেই। NOT EXISTS-এর ভেতরের চলকও একটি FROM-এ বাঁধা। ¬ সবসময় একটি সসীম তালিকার ভেতরে কাজ করে। QUEL-এর range of … is … ঘোষণাও একই উদ্দেশ্যে। Datalog-এ একই নিয়মের নাম “safe rule”: মাথার প্রতিটি চলক শরীরের কোনো ধনাত্মক atom-এ থাকতে হবে।

তবে SQL পুরোপুরি pure নয়। SELECT 1 + 1 বা SELECT cgpa * 2 নতুন মান বানায়, যা dom(P)-এ নেই। কিন্তু এগুলো সসীম সংখ্যক সারি থেকে সসীম সংখ্যক মান, তাই উত্তর তবুও সসীম থাকে। অসীমতার বিপদ SQL-এ ফিরে আসে কেবল recursive CTE-তে — যেমন থামার শর্ত ছাড়া সংখ্যা বাড়াতে থাকলে কোয়েরি থামে না (পর্ব ২৫).

মূল উপলব্ধি: যা যাচাই করা অসম্ভব, তা ব্যাকরণ দিয়ে নিশ্চিত করা হয়। SQL-এর “FROM বাধ্যতামূলক” নিয়মটি আসলে একটি safety নিয়ম।

প্র ০৩Aho আর Ullman ১৯৭৯ সালে দেখান, transitive closure relational algebra-য় লেখা যায় না। এর পরেও প্রায় দুই দশক SQL স্ট্যান্ডার্ডে recursion আসেনি। এই সময়ে মানুষ কীভাবে কাজ চালাত, আর SQL:1999-এর recursive CTE কোন ধারণা থেকে এল?

Aho আর Ullman-এর প্রবন্ধ “Universality of Data Retrieval Languages” (POPL ১৯৭৯) দুটি কাজ করে। প্রথমত, প্রমাণ করে যে কোনো algebra expression-ই যেকোনো দৈর্ঘ্যের পথ খুঁজে পেতে পারে না — প্রতিটি expression-এ join-এর সংখ্যা নির্দিষ্ট, তাই সে শুধু নির্দিষ্ট দৈর্ঘ্য পর্যন্ত পথ দেখতে পারে। দ্বিতীয়ত, প্রস্তাব করে যে query ভাষায় fixpoint অপারেটর যোগ করা উচিত: একটি অপারেশন বারবার চালানো, যতক্ষণ না ফল বদলানো বন্ধ হয়।

সেই ফাঁকা সময়ে তিনটি উপায় চলত। এক, অ্যাপ্লিকেশন কোডে লুপ: COBOL, C বা পরে Java থেকে বারবার কোয়েরি পাঠিয়ে এক ধাপ করে এগোনো — প্রতিটি ধাপে একবার নেটওয়ার্ক যাতায়াত। দুই, নির্দিষ্ট গভীরতা ধরে নিয়ে অনেকগুলো self join লেখা — “আমাদের প্রতিষ্ঠানে ৬ স্তরের বেশি বস নেই”, যা একদিন ভুল প্রমাণ হয়। তিন, vendor-এর নিজস্ব সমাধান: Oracle-এর CONNECT BY, যা ১৯৮০-এর দশক থেকেই গাছের মতো ডেটা ঘুরতে পারত।

গবেষণার দিকে এই সময়টাই Datalog-এর স্বর্ণযুগ। Datalog হলো Prolog-এর ধাঁচে লেখা, কিন্তু ডেটাবেসের জন্য সীমিত একটি যুক্তিভাষা। সেখানে ancestor-এর সংজ্ঞা দুই লাইনের: anc(X,Y) :- parent(X,Y). আর anc(X,Y) :- parent(X,Z), anc(Z,Y). Semi-naive evaluation-এর মতো কৌশল (প্রতি ধাপে শুধু নতুন সারি থেকে এগোনো) এখান থেকেই আসে। SQL:1999-এর recursive CTE-র anchor আর recursive member এই দুই নিয়মের সরাসরি প্রতিরূপ, আর আধুনিক DBMS-এর working table কৌশল semi-naive evaluation-এরই রূপ (পর্ব ২৫).

আজ PostgreSQL, SQLite (৩.৮.৩, ২০১৪ থেকে), MySQL (৮.০ থেকে), SQL Server আর Oracle (১১g R2 থেকে) সবাই recursive CTE বোঝে। বিশ্লেষণধর্মী কাজে Datalog-অনুপ্রাণিত ইঞ্জিনও ফিরে এসেছে।

মূল উপলব্ধি: একটি অসম্ভবতার প্রমাণ শুধু “না” বলে না — কী যোগ করলে “হ্যাঁ” হবে, তারও পথ দেখায়। FO-র সীমা থেকেই fixpoint, Datalog আর শেষে recursive CTE.

প্র ০৪QBE ১৯৭০-এর দশকে “প্রোগ্রামার ছাড়াই ডেটা খোঁজা”-র স্বপ্ন দেখিয়েছিল। Microsoft Access-এর query grid সেই ধারার উত্তরসূরি। তবু কেন ছবি-ভিত্তিক query লেখা SQL-কে সরাতে পারেনি, আর আজকের no-code বা AI টুলের জন্য এর শিক্ষা কী?

Zloof-এর QBE-র মূল অন্তর্দৃষ্টি গভীর ছিল: মানুষ বিমূর্ত নিয়মের চেয়ে উদাহরণে ভালো ভাবে। টেবিলের ফাঁকা কাঠামো দেখলে কলামগুলো মনে রাখতে হয় না, আর একই উদাহরণ দুই জায়গায় বসালে join-এর ধারণা হাতে-কলমে বোঝা যায়। IBM পরে এটিকে পণ্য হিসেবে বাজারে আনে, আর ১৯৯০-এর দশকে Microsoft Access, Paradox-এর মতো ডেস্কটপ ডেটাবেস ধারণাটিকে কোটি মানুষের কাছে পৌঁছে দেয়। ছোট অফিসের হিসাবরক্ষক থেকে স্কুলের কেরানি — অনেকে SQL না শিখেই রিপোর্ট বানিয়েছেন।

কিন্তু সীমাও ছিল স্পষ্ট। প্রথমত, ∀ আর একাধিক স্তরের ¬. “সব কোর্স নিয়েছেন” প্রশ্ন ছবিতে আঁকতে হলে গোনার কৌশল বা কয়েকটি আলাদা query লাগে; সাধারণ ব্যবহারকারীর কাছে সেটি SQL-এর চেয়ে কম কঠিন নয়। দ্বিতীয়ত, জটিল প্রশ্নে ছবি বড় হতে থাকে — দশটি টেবিলের জাল দেখে বোঝা কঠিন, কোনটি কোন শর্তে যুক্ত। তৃতীয়ত, ছবি version control-এ রাখা, code review করা, বা এক DBMS থেকে আরেকটিতে নেওয়া কঠিন; লেখা SQL সেখানে অনেক সহজ। এজন্য Access নিজেও প্রতিটি grid-এর পেছনে একটি SQL view রাখে, আর অভিজ্ঞ ব্যবহারকারীরা শেষে সেখানেই কাজ করেন।

আজকের no-code BI টুল আর “বাংলায় প্রশ্ন করুন, SQL পান” ধরনের AI টুলও একই প্রতিশ্রুতি দেয়। শিক্ষাটি একই: সহজ প্রশ্নে এগুলো দারুণ, কিন্তু ∀, ¬, NULL আর duplicate-এর প্রশ্নে অর্থের সূক্ষ্মতা থেকে যায়। “যাঁরা কোনো অর্ডার দেননি” আর “যাঁরা সব পণ্য কিনেছেন” — দুটির যুক্তির গঠন আলাদা, আর ইন্টারফেস যত সুন্দরই হোক, সেই গঠন কাউকে না কাউকে বুঝতেই হয়।

তাই calculus শেখা আজও অপ্রয়োজনীয় নয়। AI যে SQL দেয়, তা মনে মনে ∃, ¬∃ আর ∀-এ ফিরিয়ে পড়তে পারলেই বোঝা যায় প্রশ্নটি ঠিক বোঝা হয়েছে কি না।

মূল উপলব্ধি: ইন্টারফেস বদলায় — সূত্র, ছবি, grid, এখন স্বাভাবিক ভাষা — কিন্তু নিচের যুক্তি একই first-order logic. যিনি যুক্তিটি বোঝেন, তিনি যেকোনো ইন্টারফেসের ভুল ধরতে পারেন।

প্র ০৫অনেকে বলেন “SQL হলো tuple relational calculus-এর ইংরেজি রূপ”। এই পর্বের উদাহরণ থেকে অন্তত তিনটি জায়গা দেখান, যেখানে SQL আসলে calculus থেকে আলাদা আচরণ করে।

কথাটি গঠনের দিক থেকে সত্য, অর্থের দিক থেকে আংশিক। তিনটি বড় পার্থক্য এই পর্বেই দেখা গেছে।

১. সেট বনাম bag. Calculus-এর উত্তর একটি গাণিতিক সেট — একই tuple দুবার থাকতে পারে না। SQL-এর টেবিল আর কোয়েরির ফল multiset: FROM students s, enrollments e একজন শিক্ষার্থীকে তাঁর প্রতিটি মিলে যাওয়া ভর্তির জন্য একবার করে দেখায়। CSE301 আর ∩-এর উদাহরণে তাই DISTINCT লাগছিল। SQL এই পথ বেছে নিয়েছিল আংশিকভাবে খরচের কারণে (duplicate মুছতে sort বা hash লাগে), আংশিকভাবে কারণ COUNT বা SUM-এর জন্য duplicate দরকারি তথ্য।

২. দুই-মান বনাম তিন-মান। Calculus-এ প্রতিটি atom সত্য বা মিথ্যা। SQL-এ NULL থাকলে তুলনা UNKNOWN হয়। “সর্বোচ্চ cgpa” কোয়েরিতে Fahim-এর NULL cgpa তাঁকে ফলে ঢুকিয়ে দিয়েছিল, কারণ s.cgpa > NULL কোথাও TRUE নয়, তাই ¬∃ সত্য হয়ে গেল। Pure calculus-এ এমন সারির অস্তিত্বই নেই। ¬ আর NULL একসাথে এলে তাই SQL-এ বাড়তি শর্ত (IS NOT NULL) লাগে। NOT IN-এর ফাঁদও (পর্ব ২১) এই একই উৎস থেকে আসে।

৩. বাড়তি ক্ষমতা। SQL-এ আছে aggregate (COUNT, AVG), হিসাব (depth + 1), ORDER BY, LIMIT আর recursion — এর কোনোটিই pure calculus-এ নেই। আবার উল্টো দিকে, calculus-এ সরাসরি ∀ আছে, SQL-এ নেই।

আরও একটি সূক্ষ্ম পার্থক্য: SQL-এর FROM-এ একটি টেবিল ফাঁকা হলে পুরো cross product ফাঁকা হয়, এমনকি সেই চলক WHERE-এ ব্যবহার না হলেও। Calculus-এ এমন অব্যবহৃত চলক লেখার দরকারই পড়ে না; লিখলে সেটি একটি বাড়তি শর্ত ∃c ∈ courses, যা courses ফাঁকা হলে মিথ্যা। অর্থাৎ FROM-এ বাড়তি টেবিল রাখা মানে চুপচাপ একটি ∃ শর্ত জুড়ে দেওয়া। তাই SELECT s.name FROM students s, courses c courses ফাঁকা হলে কিছুই দেয় না।

এই পার্থক্যগুলো জানা থাকলে calculus একটি চমৎকার নকশার হাতিয়ার হয়: প্রথমে যুক্তিটি পরিষ্কারভাবে ∃, ∀ আর ¬ দিয়ে লিখুন, তারপর অনুবাদের সময় তিনটি প্রশ্ন করুন — duplicate হবে কি? NULL কোথায় ঢুকতে পারে? ফাঁকা টেবিলে কী হবে?

মূল উপলব্ধি: SQL-এর হাড়গোড় tuple calculus, কিন্তু রক্তমাংস bag, NULL আর aggregate. অনুবাদের সময় এই তিনটিই ভুলের প্রধান উৎস।

অনুশীলন

উপরের যেকোনো db-uni এডিটরে কোড বদলে নিজের উত্তর চালিয়ে দেখতে পারেন।

  1. কোয়েরি লিখুন: “CSE বিভাগের (dept_id = 1) যেসব শিক্ষকের বেতন 100000-এর বেশি, তাঁদের নাম” — প্রশ্নটি TRC, DRC আর SQL তিন রূপে লিখুন। instructors-এর কলাম: (instructor_id, name, dept_id, salary, mentor_id).
    TRC: { t | ∃i ∈ instructors ( i.dept_id = 1 ∧ i.salary > 100000
                                 ∧ t.name = i.name ) }
    DRC: { ⟨n⟩ | ∃id, s, m ( ⟨id, n, 1, s, m⟩ ∈ instructors ∧ s > 100000 ) }
    SELECT i.name FROM instructors i
    WHERE i.dept_id = 1 AND i.salary > 100000;

    ফল: Dr. Anisur Rahman. CSE-র তিন শিক্ষক 120000, 85000 আর 65000 পান; শুধু প্রথমজন 100000-এর ওপরে। Dr. Mahbub Alam-এর বেতন 110000, কিন্তু তিনি EEE-র। DRC-তে dept_id-এর ঘরে সরাসরি ধ্রুবক 1 বসেছে।

  2. হাতে মূল্যায়ন করুন (trace): r(A) = {1, 2}, s(A, B) = {(1, x), (1, y), (2, x)}, t(B) = {x, y}. সূত্র {⟨a⟩ | ⟨a⟩ ∈ r ∧ ∀b (⟨b⟩ ∈ t → ⟨a, b⟩ ∈ s)}-এর ফল কী? t ফাঁকা হলে ফল কী হবে?

    ফল {⟨1⟩}. a = 1: b = x হলে (1, x) ∈ s ✓, b = y হলে (1, y) ∈ s ✓; t-এর বাইরের যেকোনো b-তে বাঁ পাশ মিথ্যা, implication সত্য। তাই ∀ সত্য। a = 2: b = y হলে ⟨y⟩ ∈ t সত্য কিন্তু (2, y) ∉ s — প্রতি-উদাহরণ, ∀ মিথ্যা। এটিই s ÷ t.

    t ফাঁকা হলে {⟨1⟩, ⟨2⟩}. ⟨b⟩ ∈ t কখনো সত্য নয়, তাই প্রতিটি implication সত্য (vacuous truth), আর r-এর সব সারি টেকে। Division-এর সংজ্ঞাও একই ফল দেয়: ফাঁকা সেটের “সবকিছুর সাথে” জোড়া থাকা তুচ্ছভাবে সত্য।

  3. Safe না unsafe? কারণসহ বলুন: (ক) {t | t ∈ students ∨ t.cgpa > 3} (খ) {t | t ∈ students ∧ ¬∃e ∈ enrollments (e.student_id = t.student_id)} (গ) {⟨x⟩ | ¬(⟨x⟩ ∈ r)} (ঘ) {⟨x, y⟩ | ⟨x⟩ ∈ r ∧ y = 5}

    (ক) unsafe. ∨-এর ডান পাশ students-এর বাইরের যেকোনো tuple-কে ঢুকতে দেয়, যার cgpa 3-এর বেশি — অসীম। (খ) safe. t students-এ বাঁধা, ¬ শুধু সেই সসীম তালিকার ভেতরে কাজ করে; উত্তর Habib Ullah. (গ) unsafe. r-এ নেই এমন সব মান — অসীম। (ঘ) safe. x r-এ বাঁধা, আর y একটি ধ্রুবকের সমান; 5 সূত্রেই আছে, তাই dom(P)-এ পড়ে। উত্তরের সারি-সংখ্যা r-এর সারি-সংখ্যার সমান।

  4. Algebra থেকে অনুবাদ করুন: πinstructor_id(instructors) − πinstructor_id(courses) — অর্থাৎ যে শিক্ষকের কোনো কোর্স নেই। TRC আর SQL-এ লিখুন। মনে রাখুন, courses-এ ENG101-এর instructor_id NULL.
    -- TRC: {t | t ∈ instructors ∧ ¬∃c ∈ courses (c.instructor_id = t.instructor_id)}
    SELECT t.instructor_id, t.name FROM instructors t
    WHERE NOT EXISTS (SELECT 1 FROM courses c
                      WHERE c.instructor_id = t.instructor_id);

    ফল: 4 | Rumana Islam. courses-এ শিক্ষক 2, 1, 6, 6, 3, 5 আর NULL — 4 নেই। EXCEPT দিয়েও (SELECT instructor_id FROM instructors EXCEPT SELECT instructor_id FROM courses) ফল 4, কারণ EXCEPT-এ NULL শুধু NULL-কেই বাদ দেয়। কিন্তু NOT IN (SELECT instructor_id FROM courses) লিখলে ফল ফাঁকা — NULL-এর কারণে। Algebra-য় NULL নেই, তাই algebra-র − এর সবচেয়ে বিশ্বস্ত SQL অনুবাদ NOT EXISTS বা EXCEPT.

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রম“সব কোর্স নিয়েছেন” প্রশ্নকে ∀ থেকে SQL-এ নেওয়ার ধাপগুলো সাজান (▲▼ চেপে সরান)

  1. সূত্র লেখা: ∀c (P(c) → Q(c))
  2. implication সরানো: ∀c (¬P(c) ∨ Q(c))
  3. ∀-কে ¬∃¬ দিয়ে বদলানো: ¬∃c ¬(¬P(c) ∨ Q(c))
  4. De Morgan প্রয়োগ: ¬∃c (P(c) ∧ ¬Q(c))
  5. SQL লেখা: NOT EXISTS (… WHERE P AND NOT EXISTS (… Q …))

Tuple relational calculus-এ {t | ¬(t ∈ students)} expression-টি সম্পর্কে কোনটি সঠিক?

  • এর উত্তর ফাঁকা সেট
  • এটি unsafe, কারণ এর উত্তর অসীম হতে পারে
  • এটি safe, কারণ এতে শুধু একটি টেবিল আছে
  • এটি syntax-এর দিক থেকে অবৈধ, কারণ ¬ TRC-তে নেই

সূত্র ∃e ∈ enrollments (e.student_id = t.student_id ∧ e.course_id = 'CSE301')-এ কোন চলক free?

  • শুধু t
  • শুধু e
  • t আর e দুটিই
  • কোনোটিই নয়; দুটিই bound

Codd-এর উপপাদ্য (১৯৭২) কী বলে?

  • SQL আর relational algebra-র প্রকাশক্ষমতা হুবহু সমান
  • যেকোনো (safe বা unsafe) calculus algebra-র চেয়ে শক্তিশালী
  • safe TRC, safe DRC আর relational algebra-র প্রকাশক্ষমতা সমান
  • Calculus-এ লেখা প্রশ্ন algebra-র চেয়ে দ্রুত চলে

SQL-এ FOR ALL না থাকায় “সব কোর্স নিয়েছেন” লিখতে কোন যৌক্তিক সমতুল্যতা ব্যবহার করা হয়?

  • ∀x P(x) ≡ ∃x P(x)
  • ∀x P(x) ≡ ¬∃x P(x)
  • ∀x P(x) ≡ ∃x ¬P(x)
  • ∀x P(x) ≡ ¬∃x ¬P(x)

instructors(instructor_id, mentor_id) টেবিলে mentor-এর শিকল যেকোনো দৈর্ঘ্যের হতে পারে। “একজন শিক্ষকের অধীনে প্রত্যক্ষ বা পরোক্ষভাবে থাকা সবাই” — এই প্রশ্ন সম্পর্কে কোনটি সঠিক?

  • যথেষ্ট লম্বা একটি safe TRC সূত্র দিয়ে লেখা যায়
  • এটি transitive closure; pure calculus বা algebra-য় লেখা যায় না, SQL-এ recursive CTE লাগে
  • Relational division (÷) দিয়ে লেখা যায়
  • SQL-এ কোনোভাবেই লেখা যায় না

Query-By-Example (QBE)-তে দুটি টেবিলের দুটি কলামে একই example element (যেমন _S) বসানোর অর্থ কী, আর QBE কোন ভাষার দৃশ্যরূপ?

  • দুই টেবিলের cross product; QBE tuple calculus-এর রূপ
  • দুই কলামের মান আলাদা হতে হবে; QBE relational algebra-র রূপ
  • দুই কলামের মান সমান হতে হবে (join); QBE domain relational calculus-এর রূপ
  • দুটি কলামই ফলে দেখাতে হবে; QBE SQL-এর রূপ

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Algebra procedural, calculus declarative: একটি বলে কীভাবে, অন্যটি বলে কী চাই। Calculus-এর ভিত্তি first-order logic — ∧ ∨ ¬ →, ∃ ∀, free আর bound চলক।
  2. TRC {t | P(t)}-এ চলক পুরো সারি; DRC {⟨x1, …, xn⟩ | P}-এ চলক কলামের মান, আর একই চলক দুই জায়গায় মানে join. QBE হলো DRC-র ছবি।
  3. ∀c ∈ R (P → Q) ≡ ¬∃c ∈ R (P ∧ ¬Q) — SQL-এ দুই স্তরের NOT EXISTS. ∀-এর সাথে →, ∃-এর সাথে ∧; ফাঁকা ভাজকে ∀ তুচ্ছভাবে সত্য।
  4. Safe expression-এর উত্তর শুধু dom(P)-এর মান দিয়ে গড়া; {t | ¬(t ∈ R)} unsafe. SQL-এর বাধ্যতামূলক FROM safety-কে ব্যাকরণে বেঁধে দেয়।
  5. Codd-এর উপপাদ্য: safe TRC = safe DRC = relational algebra. Pure calculus-এ recursion আর aggregate নেই; SQL-এ সেগুলো বাড়তি — আর SQL-এ bag আর NULL calculus-এর অর্থ বদলাতে পারে।

পরের পর্বে: Query ভাষার তত্ত্ব শেষ, এবার ভালো টেবিল ডিজাইনের তত্ত্ব। পর্ব ৩৫-এ functional dependency — “student_id জানলে নাম জানা যায়” ধরনের নিয়ম, attribute closure, key খোঁজা আর canonical cover — normalization-এর ভিত্তি।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান