SQL to select a table through a generic relationship - sql

In a relational database I have three tables. Using SQL Server.
person(id, type)
student(id, person_id, type, student specific fields)
teacher(id, person_id, type, teacher specific fields)
Student and teacher are both people, therefore a student will have a record in both the person and student tables, as will the teacher. Student and teacher have foreign keys to person. Student and teacher have different field definitions therefore a union will NOT work.
Now I have the person's id and depending on whether the person is a student or teacher I would like to select * from the relevant table (not person).
For example, if the person is a student I would like my query to select the student table.
I can think of a few inefficient methods but I am looking for the optimum one.

I would suggest a UNION
SELECT student.*
FROM student
WHERE person_id= #id
UNION
SELECT teacher.*
FROM teacher
WHERE person_id= #id

if exists(select person_id from student where person_id = #id)
select * from student where person_id = #id
else
if exists(select person_id from teacher where person_id = #id)
select * from teacher where person_id = #id

If your RDBMS is SQLServer, then I would abstract a view along the lines of podiluska's union, mapping out specific fields in Student and Teacher to common names, and padding with NULLs where no mapping is possible
And assuming that Students and Teachers inherit from person (i.e. both are 0..1 to 1 with Person), then they can share the same primary key, i.e. no need for new surrogates keys on Teacher and Student.
I've assumed that person.type determines whether the person is a Student(S) or Teacher(T).
CREATE VIEW SubClassesOfPerson AS
SELECT p.id as PersonId,
p.name as PersonName,
p.OtherBaseFieldsHere,
s.SomeStudentSpecificField AS MappedField1,
s.SomeStudentSpecificFieldX AS MappedFieldX,
s.SomeStudentSpecificField as MappedFieldForStudentOnly,
NULL as MappedFieldForTeacherOnly -- Pad this because it can't be mapped
FROM person p
INNER JOIN student s
on s.person_id = p.id AND p.type = 'S'
UNION
SELECT p.id as PersonId,
p.name as PersonName,
p.OtherBaseFieldsHere,
t.SomeTeacherSpecificField AS MappedField1,
t.SomeTeacherSpecificFieldX AS MappedFieldX,
NULL as MappedFieldForStudentOnly, -- Pad this because it can't be mapped
t.SomeTeacherSpecificField as MappedFieldForTeacherOnly
FROM person p
INNER JOIN teacher t
on t.person_id = p.id AND p.type = 'T'

Related

Getting data from unrelated tables

I've got this homework for database 'SCHOOL'
Table Student (StudentID, Name, Surname, etc..)\
Table Application (ApplicationID, StudentID, ClassID)\
Table Class (ClassID, ClassName, TeacherID)\
Table Teacher (TeacherID, Name, Surname, etc..)
There are some other tables and columns in the database but I don't think they're important to this query.
I need to get name of the teachers for whose classes no student signed up. Let's say there is no one signed up for Math class, and I need to get the name of the teacher for that Math class.
SELECT Teacher.Name, Teacher.Surname, Class.ClassName
FROM Teacher
INNER JOIN Class ON Class.TeacherID = Teacher.TeacherID
INNER JOIN Application ON Application.ClassID = Class.ClassID
INNER JOIN Student ON Student.StudentID = Application.StudentID
WHERE Application.PredmetID IS NULL
Help would be appreciated.
I would recommend NOT EXISTS:
select t.name
from class c join
teacher t
on t.teacherid = c.teacherid
where not exists (select 1
from application a
where a.classid = c.classid
);
I need to get name of the teachers for whose classes no student signed up
Select T.name
From Teacher T inner join [Class] C on T.TeacherId = C.TeacherId
Where C.ClassId not in (Select Distinct ClassId from Application)

PostgreSQL: How do I get data from table `A` filtered by a column in table `B`

I want to fetch all parents that have kids in a specific grade only in a school.
Below are trimmed down version of the tables.
TABLE students
id,
last_name,
grade_id,
school_id
TABLE parents_students
parent_id,
student_id
TABLE parents
id,
last_name,
school_id
I tried the below query but it doesn't really work as expected. It rather fetches all parents in a school disregarding the grade. Any help is appreciated. Thank you.
SELECT DISTINCT
p.id,
p.last_name,
p.school_id,
st.school_id,
st.grade_id,
FROM parents p
INNER JOIN students st ON st.school_id = p.school_id
WHERE st.grade_id = 118
AND st.school_id = 6
GROUP BY p.id,st.grade_id,st.school_id;
I would think:
select p.*
from parents p
where exists (select 1
from parents_students ps join
students s
on ps.student_id = s.id
where ps.parent_id = p.id and
s.grade_id = 118 and
s.school_id = 6
);
Your question says that you want information about the parents. If so, I don't see why you are including redundant information about the school and grade (it is redundant because the where clause specifies exactly what those values are).

Mapping table join query

I have three tables as follows,
student
category
student_category
Student table has the following columns:
studentid, student name, studenttype
Category table has the following tables:
categoryid, categoryname, ..
student_category table has the following columns:
id, studentid, categoryid
Now I have 2 input parameters categoryid and studenttype so now I need to get the all the student details which are associated with respective categoryid and whose student type is studenttype
I tried as follows which is not giving correct result,
SELECT
s.*
FROM
student s
JOIN
student_category sc ON sc.categoryid = 1;
Also I also need to filter student whose studenttype is 'someinput'
I am working on PostgreSQL. Any suggestions please
You should add a where clause and also use the appropiate join condition.
SELECT s.*
FROM student s
JOIN student_category sc ON sc.studentid = s.studentid
where s.studenttype = 'studenttype' --or your desired value
and sc.categoryid = 1

Difficulty understanding a SQL Join query

I have these tables in my database:
Student:
id,
style_id,
instrument,
name,
Teacher:
id,
style_id,
name
lessons:
id,
style_id,
teacher_id,
student_id,
month
And I'm trying to write a query that returns every student enrolled for a particular month and their teacher and style e.g. it would return something like this:
March
Student Name Teacher Style
-------------------------------
Glen Bob Jazz
Missy Bruce Rock
So far all I got is something like this (I know I need to use joins just don't know how):
SELECT
students.name, teacher.name, style.style_name
FROM
lessons
WHERE
month = "March"
JOINS students, teachers
First off, it is not a good practice for you to have the name of the table in your primary key, in your table teacher, you used musician_id, use teacher_id instead. This is the code I would use to achieve what you are trying to do.
SELECT a.style_name, b.name, c.name, d.month
FROM lessons d
INNER JOIN Styles a ON a.style_id = d.style_id
INNER JOIN Student b ON b.student_id = d.student_id
INNER JOIN Teacher c ON c.teacher_id = d.teacher_id
WHERE d.month = "march";
Since you are in difficulty with joins, Inner join, will see if its evidence of the ids in both tables to return true, thats why it solvs your problem.
To answer your specific question,
SELECT Student.name AS Student, Teacher.name AS Teacher, Styles.style_name AS Style
FROM lessons
INNER JOIN Styles ON lessons.style_id = Styles.style_id
INNER JOIN Teacher ON lessons.teacher_id = Teacher.musician_id
INNER JOIN Student ON lessons.student_id = Student.student_id
WHERE lessons.month="March";
You might want to look at your table design, though. You have a style_id in the lessons, the Teachers and the Students tables. The query above matches the style name with the style_id in the lessons table, while it could be linked to either the Teacher table or the Student table. This could lead to reporting problems if Bob is teaching a Jazz lesson and his profile says he's a Rock teacher.
It's better to remove the style_id from the Teacher and Student tables and introduce a new table
Interests: participant_id (could be teacher id or student id), role ('teacher' or 'student'), style_id

Find the minimum value of a SQL table attribute and create a new table of attributes?

I have three SQL tables. The first is SCUBA_CLASSES with attributes CLASSID and CLASSNAME. Second is a table called STUDENTS with attributes STUDENTID and AGE. Third is a table TAKES_CLASS with attributes STUDENTID and COURSEID.
How do I create a new table that displays the CLASSID and CLASSNAME of the youngest student in the group?
I have:
CREATE TABLE YOUNGEST_STUDENT_ENROLLMENT
SELECT SCUBA_CLASSES.CLASSID AS CLASSID, SCUBA_CLASSES.CLASSNAME AS CLASSNAME
FROM SCUBA_CLASSES, STUDENTS, TAKES_CLASS
WHERE MIN(STUDENTS.AGE)
Not sure what to do from here. I have to find the youngest student and output the CLASSID and CLASSNAME of all the classes they are enrolled in.
It would seem a VIEW would be a better fit than a table. The view will update automatically when new students are enrolled;
CREATE VIEW YOUNGEST_STUDENT_ENROLLMENT AS
WITH cte AS (
SELECT SC.CLASSID, SC.CLASSNAME, S.NAME, S.AGE,
ROW_NUMBER() OVER (PARTITION BY SC.CLASSID ORDER BY AGE) RN
FROM SCUBA_CLASSES SC
JOIN TAKES_CLASS TC ON SC.CLASSID = TC.CLASSID
JOIN STUDENTS S ON S.STUDENTID = TC.STUDENTID
)
SELECT CLASSID,CLASSNAME,NAME,AGE FROM cte WHERE RN=1;
This view will always contain the person with the lowest age per class. If you want it to contain only the lowest aged person no matter which class, you can remove the PARTITION BY SC.CLASSID clause.
An SQLfidde to test with.
Try this nested query:-
CREATE TABLE YOUNGEST_STUDENT_ENROLLMENT AS (
SELECT SCUBA_CLASSES.CLASSID AS CLASSID, SCUBA_CLASSES.CLASSNAME AS CLASSNAME
FROM SCUBA_CLASSES
WHERE SCUBA_CLASSES.CLASSID IN (SELECT TAKES_CLASS.CLASSID FROM TAKES_CLASS
WHERE TAKES_CLASS.STUDENTID IN (SELECT STUDENTS.STUDENTID FROM STUDENTS
WHERE STUDENTS.AGE IN (SELECT MIN(STUDENTS.AGE) as AGE FROM STUDENTS))));