Tuple Relational Calculus
Instead of writing the recipe step by step, just describe the dish — TRC is the declarative twin of relational algebra, the same power in different clothes.
What you'll learn
- TRC is declarative — say WHAT tuples you want, not HOW to compute them
- The form { t | predicate(t) }, with ∃ and ∀ ranging over tuples of relations
- RA and TRC have the same expressive power for safe queries — same language, different style
- Reading a TRC expression: every quantified variable is a tuple; dot into its attributes
Before you start
You order coffee one of two ways. Recipe style: “grind 18 grams, brew at 93°C for 28 seconds, pour 36 grams of milk on top.” Or declarative: “a cappuccino, please.” Both get you the same drink. Relational algebra was the recipe; tuple relational calculus (TRC) is the menu — you describe what you want, and leave the steps to the database. That “describe, don’t compute” idea is exactly why SQL feels the way it does: SQL is the declarative style made practical, so getting comfortable reading TRC is really practising the mindset behind every query you’ll ever write.
The form is short:
{ t | predicate(t) }
Read it: “the set of all tuples t such that the predicate holds.” The
predicate uses logical connectives (∧, ∨, ¬) and the quantifiers ∃
(“there exists”) and ∀ (“for all”) over tuples drawn from relations.
RA vs TRC — the same query, two styles
Read the TRC side carefully. There’s an output tuple variable t, and the
predicate introduces two more tuple variables — s ranging over Student and
e ranging over Enroll. We then constrain them: t.name = s.name (the
output’s name is from a Student), s.id = e.sid (that Student is the one in
the Enroll row), and e.cid = "CS101". The set of all such t.name is the
answer — the same set of names the RA expression on the left produces.
Worked example — names of students in CS101
Schemas:
Student(id, name),Enroll(sid, cid). Return names of students enrolled in CS101.
TRC:
{ t.name | ∃ s ∈ Student, ∃ e ∈ Enroll
( t.name = s.name ∧ s.id = e.sid ∧ e.cid = "CS101" ) }
RA:
π_name( Student ⋈ σ_cid="CS101" (Enroll) )
Both walk through the same logical join: a Student row with an Enroll row whose
cid is CS101. RA names the steps; TRC names the constraints. Codd’s theorem
guarantees they have the same expressive power for safe queries — anything one
can compute, so can the other.
A “for all” in TRC
What about “students enrolled in EVERY course”? Use the ∀ quantifier directly:
{ s.name | ∃ s ∈ Student
( ∀ c ∈ Course
( ∃ e ∈ Enroll ( e.sid = s.id ∧ e.cid = c.cid ) ) ) }
Read it: a student s such that for every course c, there exists an Enroll
row matching s.id to that c.cid. This is the same shape as relational
division A ÷ B from the previous lesson — ∀ is the hallmark of for-all
queries, in either notation.
How GATE asks this
The pattern is always the same MCQ: an expression like
{ t.x | ∃ r ∈ R (…) } is given and four English sentences are options.
You translate by reading the introduction of each tuple variable, the joining
predicates between attributes, and any equality with a constant. The right
English sentence is usually a one-liner.
Quick check
Quick check
Practice this in an interview
All questionsA derived table is an inline subquery in the FROM clause that acts as a virtual table for the duration of the query; it is not correlated to the outer query and has no name reuse. A CTE is named and can be referenced multiple times, while a correlated subquery executes per-row in WHERE or SELECT.
A recursive CTE has an anchor member that seeds the recursion and a recursive member that joins back to the CTE itself; the engine iterates until no new rows are produced. It is the standard SQL approach for querying trees and graphs such as org charts, bill-of-materials, and threaded comments.