improving the sql query for a dbms problem - sql

I want to write a SQL query for the problem as defined below, my answer for the first part is as below, but I am not sure about the answer, can anyone help me? Is the answer correct, or if not, how can I improve it?
For the second part can anyone help me?
Let us consider the following relational schema about physicians and departments:
PHYSICIAN (PhysicianId, Name, Surname, Specialization, Gender, BirthDate, Department);
Let every physician be univocally identified by a code and characterized by a name, a surname, a specialization (we assume to record exactly one specializa- tion for each physician), a gender, a birth date, a department (each physician is assigned to one and only one department), and a home city.
Let every city be univocally identified by its name and characterized by the region it belongs to.
DEPARTMENT (Name, Building, Floor, Chief)
Let every department be univocally identified by a name and characterized by its location (building and floor) and chief. Let us assume that a physician can be the chief of at most one department (the department he/she belongs to). We do not exclude the possibility for two distinct departments to be located at the same floor of the same building.
BELONGS TO(City,Region)
Let us assume that a physician can be the chief of at most one department (the department he/she belongs to). We do not exclude the possibility for two distinct departments to be located at the same floor of the same building.
I want to formulate an SQL query to compute the following data (exploiting aggregate functions only if they are strictly necessary):
• the departments such that (i) all their physicians reside in the region Piemonte and (ii) at least one of them resides in the city Torino.
My answer for the fist part is as below:
for the second part, I don't know how to solve it .
create view X{
select b.city, b.region, b.department
from physician p inner join belong-to b on b.city= p.homecity
where b.region="Piemonte"
select name
from department d
where exists( select*
from X
where p.department =d.name )

I think this should work:
Select Distinct b.department
From physician p
Join belong-to b on b.city = p.homecity
Where Exists (Select 1 From belong-to b2 Where b2.city = p.homecity AND b2.region = "Piemonte")
Exists (Select 1 From belong-to b2 Where b2.city = p.homecity AND b2.city = "Torino") AND
NOT Exists (Select 1 From belong-to b2 Where b2.city = p.homecity AND b2.region <> "Piemonte") AND

I would use aggregation:
select p.department
from physician p join
belongsto b
on p.homecity = b.city
group by p.department
having sum(case when b.region <> 'Piemonte' then 1 else 0 end) = 0 and -- no Piemonte
sum(case when p.homecity = 'Torino' then 1 else 0 end) >= 1 -- at least one Torino

Related

Postgres Question: Aren't both a and b correct?

For questions below, use the following schema definition.
restaurant(rid, name, phone, street, city, state, zip)
customer(cid, fname, lname, phone, street, city, state, zip)
carrier(crid, fname, lname, lp)
delivery(did, rid, cid, tim, size, weight)
pickup(did, tim, crid)
dropoff(did, tim, crid)
It's a schema for a food delivery business that employs food carriers (carrier table).
Customers (customer table) order food from restaurants (restaurant table).
The restaurants order a delivery (delivery table); to deliver food from restaurant to customer.
The pickup table records when carrier picks up food at restaurant.
The dropoff table records when carrier drops off food at customer.
1.Find customers who have less than 5 deliveries.
a. select cid,count()
from delivery
group by cid
having count() < 5;
b. select a.cid,count()
from customer a
inner join delivery b
using(cid)
group by a.cid
having count() < 5;
c. select a.cid,count()
from customer a
left outer join delivery b
on a.cid=b.cid
group by a.cid
having count() < 5;
d. select cid,sum(case when b.cid is not null then 1 else 0 end)
from customer a
left outer join delivery b
using (cid)
group by cid
having sum(case when b.cid is not null then 1 else 0 end) < 5;
e. (write your own answer)
No, they are not correct. They miss customers who have had no deliveries.
The last is the best of a bunch of not so good queries. A better version would be:
select c.cid, count(d.cid)
from customer c left outer join
delivery d
on c.cid = d.cid
group by c.cid
having count(d.cid) < 5;
The sum(case) is over kill. And Postgres even offers a better solution than that!
count(*) filter (where d.cid is not null)
But count(d.cid) is still more concise.
Also note the use of meaningful table aliases. Don't get into the habit of using arbitrary letters for tables. That just makes queries hard to understand.

how to count different values from different tuples into the same sceme in sql

I have a table of hospitals details, department details linked to it, types of workers (the staff) in different tables, and their salary information.
I want to extract the following for each hospital: the average and the sum of the salaries, the number of nurses, the number of research doctors and the number of beds in all the departments of a specific hospital.
I built this view of all the workers salary information:
CREATE VIEW workers AS
SELECT hospcod, docsal as sal, 'treatdoc' as typework
FROM doc NATURAL JOIN treatdoc NATURAL JOIN dept
UNION
SELECT hospcod, nursal, 'nurse'
FROM nurse NATURAL JOIN dept
UNION
SELECT hospcod, docsal, 'rsrchdoc'
FROM doc NATURAL JOIN rsrchdoc NATURAL JOIN lab;
the departments and the labs have the hospital code column to correlate a worker information to a specific hospital.
so I have one sceme for all the staff with their rules workers(hospital_code, salary, type_of_worker)
here is the query I'm trying to build:
SELECT hospname, sum(workers.sal), avg(workers.sal), count(dept.numbed),
(SELECT count(typework) from workers where typework = 'nurse') nurse_num,
(SELECT count(typework) from workers where typework = 'rsrchdoc') rsrchdoc_num
FROM hosp NATURAL JOIN dept NATURAL JOIN workers
GROUP BY hospname;
I want to count for each hospital, the number of nurses and the number of research doctors
but it should be correlated somehow to the different hospitals (in the above it gives me the same number of nurses / rsrchdocs for each hospital) , there should be columns that is grouped by hospnames and should get all the tuples like the salary info (avg, sum), as I got properly, but the workers information should be grouped HAVING typework = 'nurse' for the nurse_num, and for the column rsrchdoc_numit should be HAVING typework = 'rsrchdoc_num'
does someone have an idea how can I combine thouse columns in one query?
thank you!
There is an error in your query, I will try to explain.
When you do:
(SELECT count(typework) from workers where typework = 'nurse') nurse_num,
You are getting a constant, that is not affected by the "group by" you are doing after.
What you have to do is a JOIN (like you did in the view) and link the nurse and the rsrchdoc to an specific hospital.
I will give an example is pseudo code
SELECT hosp_name, sum(nurse.salary) , avg(nurse.salary)
FROM hosp
JOIN nurse ON nurse.hosp_name = hosp.hosp_name
GROUP BY hosp.hosp_name
This query will give you 1 row for each nurse in each hospital (assuming that a nurse may work in more than one hospital).
Then you have to do the same thing also for doctors, in a different operation.
SELECT hosp_name, sum(doctors.salary) , avg(doctors.salary)
FROM hosp
JOIN doctors ON doctors.hosp_name = hosp.hosp_name
GROUP BY hosp.hosp_name
And finally you will have to join both ( you may perform the sum first to make it more readable.
SELECT hosp_name, sum_sal_doc, avg_sal_doc, sum_nur_doc, avg_nur_doc
FROM hosp
LEFT JOIN ( SELECT doctors.hosp_name, sum(doctors.salary) as sum_sal_doc, avg(doctors.salary) as avg_sal_doc
FROM doctors
GROUP BY doctors.hosp_name
) t1 ON t1.hosp_name = hosp.hosp_name
LEFT JOIN ( SELECT nurses.hosp_name, sum(nurses.salary) as sum_nur_doc, avg(nurses.salary) as avg_nur_doc
FROM nurses
GROUP BY nurses.hosp_name
) t2 ON t2.hosp_name = hosp.hosp_name
There must be 1 to many relationship between hosp --> dept and hosp --> workers so if you join these 3 tables then you will definitely find the duplicates for dept and workers so you must have to create sub-query for one of the dept or workers to fetch single grouped record group by hospital as follows:
SELECT h.hospname,
sum(w.sal) total_all_worker_sal,
avg(w.sal) avg_all_workers_sal,
d.numbed,
count(case when w.typework = 'nurse' then 1 end) nurse_num,
count(case when w.typework = 'rsrchdoc' then 1 end) rsrchdoc_num
FROM hosp h
JOIN (select hospital_code , sum(numbed) numbed
-- used SUM as numbed must be number of bed in department
-- COUNT will give you only number of department if you use count(d.numbed)
from dept
group by hospital_code) d ON h.hospital_code = d.hospital_code
JOIN workers w ON h.hospital_code = d.hospital_code
GROUP BY h.hospital_code , h.hospname, d.numbed;
-- used h.hospital_code to separate the records if two hospitals have same name

Selecting cities that have 10 or more students and instructors combined in SQL

I need to show the city, state, number of student residents, number of instructor residents, and total student/instructor residents in that city. The information is contained in 3 tables: ZIPCODE, STUDENT, and INSTRUCTOR.
The ZIPCODE table has the columns ZIP, CITY, and STATE.
The STUDENT table has STUDENT_ID and ZIP.
The INSTRUCTOR table has INSTRUCTOR_ID and ZIP.
I've tried a couple of inner joins, and intersects, but I keep getting a wide variety of errors. I'm still very new with SQL, and am not sure how to actually make this work, any help or advice would be greatly appreciated.
You probably want a mix of union and join for this. I doubt you want intersect. Plenty of ways to do this, here's one
SELECT
Z.city,
Z.state,
SUM(case when d.typ = 's' then 1 ELSE 0 END) as count_students,
SUM(case when d.typ = 'i' then 1 ELSE 0 END) as count_instructors,
Count(*) as count_all
FROM
(SELECT * FROM
(SELECT 's' as typ, zip FROM student)
UNION ALL
(SELECT 'I ' as typ, zip FROM Instructor)
) d
INNER JOIN
zipcode z
ON d.zip on z.zip
GROUP BY
z.city, z.state
I pull all the records out of each student and instructor table and union them to make one big list, make a column to keep track of the type, the sum does the counting, when the type is s, the case when returns a 1. The sum will sum the 1s up as a count. You thus end up with a city/state/typ combination for each row and when grouped on city and state and summed on the typ, it gives a count
Here's another way to do this:
SELECT
Z.city,
Z.state,
SUM(s.ct) as count_students,
SUM(i.ct) as count_instructors,
SUM(s.ct) + SUM(I.ct) as count_all
FROM
zipcode z
LEFT OUTER JOIN
(SELECT zip, count(*) ct FROM student GROUP BY zip) s
ON s.zip = z.zip
LEFT OUTER JOIN
(SELECT zip, count(*) as ct FROM Instructor GROUP BY zip) i
ON i.zip = z.zip
GROUP BY z.city, z.state
We group and count the students and the instructors in their own subqueries producing just a single count per zip and join these (left join) to all the zip codes. We group in a sub query to ensure that there is only ever a 1:1 relationship between zipcode and s/i. If it were 1:many the sums would beome distorted. Because multiple zips can refer to one city there is another round of grouping and summing to aggregate all the zips from one city

Oracle SQL: Nested sub-queries, outer are the same, inner differs

I'm in a situation where I have several sub-queries, joined by UNION, each nested with an inner sub-query. The outer sub-queries are fully identical to each other, while the inner queries differ and are unique.
Reuse of the entire outer sub-queries is cumbersome for reading and making changes, and it would be greatly beneficial if they could be defined once and reused. So this is a question about creating reusable SQL-queries, but with distinct inner sub-queries that are passed on as arguments.
For my examples, I will present a simplified case that has the same issue as my real code.
We're using Oracle SQL for our project.
Say that we have a database for a school or university, with the tables PERSON, STUDENT, GRADE and COURSE, and all are connected by FK-relationships.
I need to run a query which gathers a list, counting up the number of people once for every criteria:
Number of students whose last name begin on the letter 'E'
Number of students older than 20 years
Number of people (including but not limited to students) who are female
Number of students who passed the course "Intermediate Norwegian" with grade B or higher
The expected outcome:
| Description | Number_of_students
1 | Last names beginning with letter "E" | 32
2 | Older than 20 years | 154
3 | All female persons | 356
4 | Passed "Intermediate Norwegian" with grade >= B | 12
Below is a query which should satisfy what I need.
It consists of several sub-queries joined by UNION, and all have their own distinct inner query.
The code is far from brilliant, but that is beside the point. The real question is about drastically improving readability. The outer sub-queries have the same structure that could be re-used, but the inner ones are different.
SELECT * FROM
-- 1st entry: Number of students on the last name 'E'
(SELECT 'Last names beginning with letter "E"' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p1.ID FROM PERSON p1, STUDENT s1
WHERE p1.ID = s1.PERSON_ID AND p1.LASTNAME LIKE 'E%'
)
)
UNION
-- 2nd entry: Number of students older than 20 years
(SELECT 'Older than 20 years' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p2.ID FROM PERSON p2, STUDENT s2
WHERE p2.ID = s2.PERSON_ID AND p2.AGE > 20
)
)
UNION
-- 3rd entry: Number of female persons, including but not limited to students
(SELECT 'All female persons' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p3.ID FROM PERSON p3 WHERE p3.GENDER = 'Female'
)
)
UNION
-- 4th entry: Students who passed the course "Intermediate Norwegian" with grade B or higher
(SELECT 'Passed "Intermediate Norwegian" with grade >= B' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p4.ID FROM PERSON p4, STUDENT s4, GRADE g4 AND COURSE c4
WHERE p4.ID = s4.PERSON_ID
AND s4.ID = g4.STUDENT_ID
AND g4.COURSE_ID = c4.ID
AND (g4.GRADE = 'A' OR g4.GRADE = 'B')
AND c4.COURSE_NAME = 'Intermediate Norwegian'
)
)
Like I said, the code is far from brilliant. I won't be surprised if some of you cringed at what you just read.
For instance, the entire fourth one could easily be replaced by a query where you replace the entire inner query with g.GRADE = 'A' OR 'B' and c.COURSE_NAME = 'Intermediate Norwegian'.
But like I said, that is not the point here.
Every outer sub-query has the same structure:
(SELECT 'Passed "Intermediate Norwegian" with grade >= B' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
-- Inner Sub-query here
)
While every Sub-query has an inner one that differs from each other. Like the 1st and the 3rd one:
SELECT DISTINCT p1.ID FROM PERSON p1, STUDENT s1 WHERE p1.ID = s1.PERSON_ID AND p1.LASTNAME LIKE 'E%'
and
SELECT DISTINCT p3.ID FROM PERSON p3 WHERE p3.GENDER = 'Female'
What I need:
The real code I'm working with is far more complex, but has the same following issues as presented in the example above.
The result must be a list with several numbers, each categorized by their own distinct criteria (preferably described in the first column).
It consists of several sub-queries, joined by UNION
Each of these sub-queries are completely identical, with the exception of an inner sub-query that completely unique, and different from the others.
The resulting code is a huge beast, but could in theory be made far more readable if the outer code had been written only once, and reused with different inner code passed on as arguments.
I have recently come across the WITH-clause in Oracle SQL.
Something similar to this following change would be very beneficial:
WITH outer_sub_query AS (
SELECT 'DESCRIPTION HERE' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
-- INSERT INNER SUB-QUERY HERE
)
)
SELECT * FROM (
outer_sub_query -- Last Names beginning with letter 'E'
UNION
outer_sub_query -- Age > 20
UNION
outer_sub_query -- All female
UNION
outer_sub_query -- Passed that course with grade >= B
)
Unfortunately, my needs are not yet satisfied. I still need to pass on the inner sub-queries, as well as descriptions. Something similar to this:
SELECT * FROM (
outer_sub_query(
'Last names beginning with letter "E",'
SELECT DISTINCT p1.ID FROM PERSON p1, STUDENT s1
WHERE p1.ID = s1.PERSON_ID AND p1.LASTNAME LIKE 'E%'
)
UNION
outer_sub_query(
'Older than 20 years.'
SELECT DISTINCT p2.ID FROM PERSON p2, STUDENT s2
WHERE p2.ID = s2.PERSON_ID AND p2.AGE > 20
)
UNION
outer_sub_query(
'All female persons'
SELECT DISTINCT p3.ID FROM PERSON p3 WHERE p3.GENDER = 'Female'
)
UNION
outer_sub_query(
'Passed "Intermediate Norwegian" with grade >= B'
SELECT DISTINCT p4.ID FROM PERSON p4, STUDENT s4, GRADE g4 AND COURSE c4
WHERE p4.ID = s4.PERSON_ID
AND s4.ID = g4.STUDENT_ID
AND g4.COURSE_ID = c4.ID
AND (g4.GRADE = 'A' OR g4.GRADE = 'B')
AND c4.COURSE_NAME = 'Intermediate Norwegian'
)
)
The questions:
Now, defining a FUNCTION easily comes to mind. But it still brings me some questions:
It first glance, it seems that WITH-clause does not take in parameters that can be passed on. Is there any other pre-existing clauses or functions in SQL or Oracle SQL that handles this?
Is it possible to extract the inner sub-query out from the outer one, and still achieve the same result? (Remember: No changes in the outer sub-query itself).
If I am to define a FUNCTION that handles this, is it possible to pass on pure SQL-codes like I have done above?
Is there any other smart solutions that I am missing?
Thank you for your advice(s).
A common table expression (such as you already suggested) seems a likely approach for reducing code duplication in your case, but you're trying to get it to do too much for you. CTEs cannot be parameterized in the way you hope; if they were, then a use such as you envision would no longer have them in common.
Yes, you could write a table-valued function, but that seems way overkill, and it could well be difficult for the query planner to analyze. Here's about as far as you can go with a CTE:
WITH student_grades AS (
SELECT
p.id AS id,
p.lastname AS lastname,
p.age AS age,
p.gender AS gender,
c.course_name AS course_name,
g.grade AS grade
FROM
-- You really, really should use ANSI JOIN syntax:
PERSON p
JOIN STUDENT s ON p.ID = s.PERSON_ID
JOIN GRADE g ON s.ID = g.STUDENT_ID
JOIN COURSE c ON g.COURSE_ID = c.ID
-- WHERE ... other complex code here
)
You might then continue your query with ...
-- 1st entry: Number of students on the last name 'E'
SELECT
'Last names beginning with letter "E"' AS Description,
count(distinct sg1.id) AS Number_of_students
FROM student_grades sg1
WHERE sg1.lastname LIKE 'E%'
UNION
-- 2nd entry: Number of students older than 20 years
SELECT
'Older than 20 years' AS Description,
count(distinct sg2.id) AS Number_of_students
FROM student_grades sg2
WHERE sg2.AGE > 20
UNION
-- 3rd entry: Number of female persons, including but not limited to students
-- NOTE: THIS ONE MATCHES YOUR ORIGINAL, WHICH IS INCORRECT
SELECT
'All female persons' AS Description,
count(distinct sg3.id) AS Number_of_students
FROM student_grades sg3
WHERE sg3.GENDER = 'Female'
UNION
-- 4th entry: Students who passed the course "Intermediate Norwegian" with grade B or higher
SELECT
'Passed "Intermediate Norwegian" with grade >= B' AS Description,
count(distinct sg4.id) AS Number_of_students
FROM student_grades sg4
WHERE
sg4.COURSE_NAME = 'Intermediate Norwegian'
AND sg4.grade IN ('A', 'B')
And that's actually a significant improvement. Note in particular that you don't need to pass conditions, subquery or not, into the CTE; instead, you query the CTE (which you could also join to other tables, etc.). Of course, in part that's because your "inner" subqueries were a pretty horrible way of doing things; instead, I use count(distinct sg.id), which achieves the same thing as those subqueries as long as person.id is non-null, which I presume it is on account of being a PK.
But note also that even the need for a distinct count (and the bugginess of the third part of the query) arise from trying to do all four parts with the same common intermediate results in the first place. You don't need to join course or grade information in order to query information related strictly to personal characteristics, and as long as student has a 0,1:1 relationship with person, leaving out the course and grade information would give you a distinct count for free.
And as for the third part, joining the student table restricts your results to students, which you didn't want. The fact that you don't put that restriction in the "inner" subquery is irrelevant; you're using that subquery to filter results that only include people who are students in the first place. Thus, *your approach cannot produce the results you want in this case.**
Maybe your desire to factor out a big chunk of common query arises from the mysterious "other complex code". I don't see how such a thing applies to the question as you've presented it, but I'm inclined to suspect that you would be better off finding a way -- or maybe separate ways per item -- to simplify or eliminate that code. If it were the case that that code could be ignored then I might write your query like so:
WITH student_person AS (
SELECT
p.lastname AS lastname,
p.age AS age,
p.gender AS gender,
s.id AS student_id
FROM
PERSON p
JOIN STUDENT s ON p.ID = s.PERSON_ID
)
-- 1st entry: Number of students on the last name 'E'
SELECT
'Last names beginning with letter "E"' AS Description,
count(*) AS Number_of_students
FROM student_person sp1
WHERE sp1.lastname LIKE 'E%'
UNION ALL
-- 2nd entry: Number of students older than 20 years
SELECT
'Older than 20 years' AS Description,
count(*) AS Number_of_students
FROM student_person sp2
WHERE sp2.AGE > 20
UNION ALL
-- 3rd entry: Number of female persons, including but not limited to students
-- NOTE: THIS ONE MATCHES YOUR ORIGINAL, WHICH IS INCORRECT
SELECT
'All female persons' AS Description,
count(*) AS Number_of_students
-- must select from PERSON, not STUDENT_PERSON:
FROM person p2
WHERE p2.GENDER = 'Female'
UNION ALL
-- 4th entry: Students who passed the course "Intermediate Norwegian" with grade B or higher
SELECT
'Passed "Intermediate Norwegian" with grade >= B' AS Description,
count(distinct sp3.student_id) AS Number_of_students
FROM student_person sp3
JOIN grades g ON sp3.student_id = g.student_id
JOIN course c ON g.course_id = c.id
WHERE
c.COURSE_NAME = 'Intermediate Norwegian'
AND g.grade IN ('A', 'B')
Take it from here, I'm sure you'll manage.
select count(case when lastname like 'e%' then 1 end) as lastname_starts_with_e
,count(case when age > 20 then 1 end) as age_greater_than_20
,count(case when gender = 'Female' then 1 end) as is_female
from person
;

How to find classmates of student C who are taking any of the classes that C is taking, in SQL?

There are two tables, students and courses. The students table has name, age and ssn.
Courses has ssn and Course.
Now the ssn in the courses table is NOT unique but the one in students is primary key.
I need to find out the students who are taking any of the classes that student C is taking (including C's name as well in the resulting solution).
It's a challenge problem so I can just not mind it but I want to find out what the solution is.
So far I've tried to use a union operator like this :
SELECT ssn
FROM courses
UNION SELECT ssn
FROM students
WHERE name = 'c'
All this does is it returns all the ssns from the table courses. The question asks us to effectively find the set UNION of the both tables to determine the solution.
Any help is appreciated!
SELECT DISTINCT s2.ssn, s2.name
FROM students s1
INNER JOIN courses c1 /* Find all courses taken by student 'c' */
ON s1.ssn = c1.ssn
INNER JOIN courses c2 /* Find all students in all of c's courses */
ON c1.course = c2.course
INNER JOIN students s2 /* Get the details for each of those students */
ON c2.ssn = s2.ssn
WHERE s1.name = 'c'
select distinct c1.ssn, s1.name from courses c1 // find distinct ssn and name for courses X (defined below)
inner join students s1 on c1.ssn=s1.ssn
where c1.course in
(select course
from courses // use to find all courses (say X) for student c
where ssn =
(select ssn from students where name='c')) // find ssn of student
Hope this helps