This is as far as I got.
The Q3 you calculate actually holds passengers who traveled in more than one class on the same flight number on the same day. Moreover, according to the constraints there aren't any such passengers. Here's why:
According to your code Q1 is
/* (tuples where)
p_id took f_no on f_date in class
AND p_id took f_no on f_date in class'
*/
Trip JOIN RENAME class\class' Trip
For Q1, passenger p_id took f_no on d_no in class and (for that flight number and date) in class'. (Note that under common sense, with a person only able to fly a trip in one class at a time, if class <> class' then they must have flown multiple trips with the same flight number on the same date, in different classes.)
Q1 - Q2
is just SELECT class <> class' Q1
. So Q3 holds ids of passengers who traveled with different classes with the same flight number on the same date. But those people aren't relevant to a sensible interpretation of your overall query "passengers who have flown from Narita in each of these classes at least once".
But anyway since {f_no, f_date} is a CK (candidate key) of Flight, there's only one flight for a given flight number and date, so no passengers can have flown the same flight number & date more than once. So Q3 is empty anyway.
Forming a relational algebra query from an English description
Always characterize a relation--the value of a given one or of a query (sub)expression--via a statement template--predicate--parameterized by attributes. The relation holds the tuples that make it into a statement--proposition--that is true of the situation.
You must have been given the predicate for each base relation. Eg:
-- (tuples where) p_id took f_no on f_date in class
Trip
Then you need to express your query (sub)expression predicates in terms of the base predicates so that the (sub)expression relations can be calculated in terms of the base relations:
Consider classes that passengers have occupied on flights from Narita.
/* (tuples where)
FOR SOME p_id, f_no, f_date, f_orig & f_dest,
p_id took f_no on f_date in class
AND f_no flew on f_date from f_orig to f_dest
AND f_orig = 'Narita'
*/
PROJECT class SELECT f_dest = 'Narita' (Trip JOIN Flight)
The predicate of r JOIN s
is predicate-of-r AND predicate-of-s
. The predicate of SELECT c r
is predicate-of-r AND c
. Every relation operator has such a predicate transform. The predicate of PROJECT some-attributes-of-r r
is FOR SOME other-attributes-of-r predicate-of-r
. The predicate of RENAME a\a' r
is predicate-of-r
with (appropriate occurrences of) a
replaced by a'
.
To query, find some predicate equivalent to your desired predicate, then replace its parts by corresponding relation expressions. See this.
Constraints & querying
We must know the predicates in order to query. The constraints (including FDs, CKs, PKs and FKs) are truths in every situation/state that can arise, expressed in terms of the predicates. We only need to know constraints when querying if the query's predicate can only be phrased in terms of base predicates because those constraints hold. Eg given Trip & Flight but no constraints we can't query for "classes that passengers have occupied on flights from Narita", ie the classes in tuples where:
p_id took f_no on f_date in class from f_orig to f_dest
The closest we can get is (Trip JOIN Flight
):
p_id took f_no on f_date in class
AND f_no flew on f_date from f_orig to f_dest
but that doesn't necessarily tell us what class(es) were used on what flights. But if {f_no, f_date} is unique in Flight, which is implied by {f_no, f_date} being a CK of Flight, then the two predicates mean the same thing (ie have the same truth value for every tuple & situation).
On the other hand, since we can express that query given the CK constraint, we don't also need to be told that {f_no, f_date} is a FK from Trip to Flight. The FK says that if some passenger took f_no on f_date in some class then f_no flew on f_date from some origin to some destination and that {f_no, f_date} is a CK of Flight. So a Passenger {f_no, f_date} is a Flight {f_no, f_date}. But whether or not that first conjunct of the FK also holds, or any other constraint also holds, the query returns the tuples satisfying its predicate.