Create view from tables with generalization relationship - sql

SQL newbie here and I need help with a complex(?) view for my assignment. It's a database which allows teachers to set the grades and number of times a student was absent, for each class the teacher is responsible.
My tables are:
USERS (user_id, name, surname)
STUDENTS(student_id (fk to users.user_id), student_number number)
TEACHERS(teacher_id (fk to users.user_id), title varchar2)
CLASSES(class_id, subject, teacher_id(fk to teacher.teacher_id)
CLASS_REGISTRATION(class_id (fk to classes.class_id), student_id (fk to students.student_id), grade, absent_number)
I am trying to create two views. One where the student can see the class.subject, class.teacher, class_registration.grade, class_registration.absent_number, for every class he has registered.
The other view is for the teachers where they can view, student name, class subject, grade, absent_number, for every class the teacher is responsible.
The generalisation of users -> students, teachers is required and that's what's mostly giving me problems.
EDIT: Here is a query I wrote which seems to give the desired result for the teacher view, I am not sure it the best way though
CREATE OR REPLACE FORCE VIEW "TEACHER_VIEW" ("NAME", "SURNAME", "SUBJECT", "GRADE", "ABSENT") AS
select u.name, u.surname, c.class, r.grade, r.abstent
from users u, class_registration r, students s, class c
where u.user_id = s.student_id
and s.student_id = r.student_id
and c.class_id = r.class_id
/
/

I am not giving you the create view statement but here are some links for you which can help you to learn view and it's related concept.
first
second
And go through these links and as it's your assignment so try to do it by yourself.
And if you face any problems post here.

Related

SQL Recursive Query | multiple Tables Foreign Keys

Scenario
I have a few tables, each table represents an entity of a unique type. For example lets go with:
School, Subject, Class, Teacher. Listed in order as Parent -> Child
Schema
Each table has:
ID: UUID
Name: CHAR VARYING
{parent}_id: UUID<-- example, class would have Subject_id, or Teacher would have Class_id.
The {parent}_id is the foreign id for each table.
Problem
I want to make a query that lists all the teachers of a given school. In order to do this in this Schema, I need to first query Subject by School_id, then Class by subject_id and then finally teacher by class_id.
A recursive functions makes sense to me but all tutorials I find are doing this within a single table and by ids which don't change with each recursion. In my example, each recursion I will need to search for a different ID.
Question
How do you go about doing this? I could make an array of the ids and make an index, increase index and use that to access the id in the array. This however seems like a common query so I believe there might be a more elegant solution.
Note: I am using PostgreSQL
Edit for Comment
I am using PostgreSQL DB and PGAdmin
Why would UUID not work? It has worked up to this point with no problems; even works with cascading delete using foreign keys.
I can show actual schema. However here is a fictitious layout. Quite straight forward I hope.
School
ID
Name
Subject
ID
Name
School_ID
Class
ID
Name
Subject_ID
Teacher
ID
Name
Class_ID
Expected output
Teacher_ID, Teacher_Name, Class_Name, Subject_Name, School_Name
Something like?:
select
Teacher_ID, Teacher_Name, Class_Name, Subject_Name, School_Name
from
school
join
subject
on
school.id = subject.school_id
join
class
on
class.subject_id = subject.id
join
teacher
on
teacher.class_id = class.id

SQL Server - Question about Inheritance in Database Schemas

I'm having problems understanding the class table inheritance structure that you can implement using database tables. Info on class table inheritance. I have a use case where I have quite different types of persons that I need to model, but they have very minor differences. For example, all of these persons, like Student, Professor and so on, have a surname and a lastname. My first thought was to move theses attributes into a different table inside a base table like you would do in Object Oriented Programming. Here to illustrate further:
Right now, a Professor can only have one person, for example, otherwise it wouldn't make sense in my use case. Also, I have a school table that has two foreign keys, one for the Professor and one for the Student. Lets assume that a school can also have only one professor and one student. This is not the real use case that I have. This example just represents the relation in my real use case which would be too much to explain here.
What I don't understand is how you would collect data based on that. I'm trying to make a SQL Server View where I want to load the Person of the Professor and the Person of the Student from the view point of the School Table. For example:
SELECT
School.professor_id
surname,
lastname
FROM dbo.School AS school
INNER JOIN dbo.Professor as prof
ON school.professor_id = prof.ID
INNER JOIN dbo.Person as prof_person
ON prof.person_id = prof_person.ID
I can output the surname and lastname of the professor, but now I am stuck since I can't figure out how to get the person of the student.
A subtype table typically shares a key with the supertype table, instead of having its own PK and a FK. EG Student.ID is both the PK and the FK.
Then just join Student>Person in addition to Professor>Person, eg
SELECT
School.Id,
prof_person.surname prof_surname,
student_person.surname student_surname
FROM dbo.School AS school
INNER JOIN dbo.Professor as prof
ON school.professor_id = prof.ID
INNER JOIN dbo.Person as prof_person
ON prof.ID = prof_person.ID
INNER JOIN dbo.Student as student
ON school.student_id = student.ID
INNER JOIN dbo.Person as student_person
ON student.ID = student_person.ID
INNER JOIN is associative, so no need for special ordering or parentheses.

SQL question: how to find rows that share all of the same rows in a composite table?

I'm working on my SQL project using the Oracle database for class, and I'm asked a question that I see far too often.
You have three tables:
STUDENT: SNO, SNAME
CLASS: CNO, CNAME
ATTENDANCE: SNO, CNO, Grade
The question I keep finding is of a similar type: Find the names of the students that attend in all of the classes that "John" (or anyone else) attends.
John attends three classes, so I have to find the students that also attend those three classes (could be more, but those three must be there). However, I won't always know how many classes John (or whoever) attends, so it can't be hardcoded like that.
SELECT jclass.CNO
FROM attendance jclass
INNER JOIN student on jclass.SNO = student.SNO
WHERE student.SNAME = 'John';
This gets me the classes that John attends. I tried to add the identifier for the other students:
SELECT student.SNAME
FROM student
INNER JOIN attendance on student.SNO = attendance.SNO
INNER JOIN class on attendance.CNO = class.CNO
WHERE student.SNAME <> 'John'
AND class.CNO IN (SELECT jclass.CNO
FROM attendance jclass
INNER JOIN student on jclass.SNO = student.SNO
WHERE student.SNAME = 'John');
However, this only gets me the students that appear in at least one of John's classes, rather than all of them. I can see why it's doing this, but I'm not sure how to fix it. It's the one big struggle I'm having with SQL.
Here is one way - assuming SNO is primary key in the first table, CNO is primary key in the second table, and (SNO, CNO) is (composite) primary key in the third table, and that the input student is given by a unique identifier (first name is distinctly NOT a unique identifier, so the problem stated in terms of giving "John" as the input makes no sense). Here I assume the "special" student is identified by SNO = 1001; you can make 1001 into a variable, or change it to a subquery that selects a (unique!!) SNO based on some other inputs.
I didn't try to make the query as efficient as possible, or use features you most likely haven't seen in your class. Rather, I tried to make it as elementary and as readable as possible.
select sno
from attendance
where cno in (select cno from attendance where sno = 1001)
group by sno
having count(*) = (select count(*) from attendance where sno = 1001)
;
The strategy is simple: the subquery in the in condition finds the classes attended by the "special" student, then from the attendance table we select only rows for those classes. Group by student, and count. Keep only the students for whom the count is equal to the total count for the "special" student. Note the last condition is about groups, not about input rows, so it belongs in the having clause.

Filtering out records in SQL with a join

I am creating an app that makes guest lists for greek life events at universities.
The two tables I am working with are 'student' table and 'participant' table.
The fields in the student table are: student_id, student_name, university, and chapter.
Students with chapter id's are considered members, and students without chapter id's are considered guests when making guest lists(participant table).
The participant table fields are: participant_id, member(which is related to student_id), guest (which is also related to student_id), and event.
When trying to add guests to the guest list for an event, I wrote the following sql query to filter out students from different universities and that aren't in chapters and weren't already on the list:
$student = getColumn("SELECT guest FROM participant WHERE event = '$event'");
$university = getSqlValue("SELECT university FROM student WHERE student_id = '$member'");
$f->setOption('filter',
"SELECT student_name
FROM `student`
LEFT JOIN participant ON student.student_id = participant.guest
WHERE student.chapter = ''
AND student.university = '$university'
AND participant.guest != '$student'");
So, I know this isn't going to work, because I have a whole array for $student, but even if I try it with one student id, the query doesn't work. It returns empty. If I remove the last AND particpant.guest!= $student, then the query returns all the students at the university that are not members of a chapter.
My question has two parts:
Why wouldn't that query work with one value for student?
Can someone think of a better way to go about doing this?

SQL query that limits results based on appearance in other tables

I'm a SQL novice, but need to write some SQL statements for a Java program that has to interact with a database. Our Java textbook covers only very basic SQL commands, and I am having trouble getting a more advanced (by my standards) one to work.
Here's the situation:
The database has 5 tables.
Teacher: TeacherID (PK), LastName
Class: ClassID (PK), Description
Room: Building, Room Number, PK is the combo of those two
TeachingAssignments: TeacherID(FK), ClassID(FK)
ClassRoomAssignments: ClassID(FK), Building, Room Number(combo is FK)
I need to give just the LastName, ClassID, and Building of only those teachers, classes, and rooms that are fully assigned. I.e., if a class both has a teacher and a room assignment, then I need to give that class's ID, the assigned teacher's last name, and the assigned building.
I have little idea how to proceed.
I've been playing around with statements like the following but they aren't working for me:
SELECT Teacher.LastName, Class.ClassID, Room.Building
FROM Teacher, Class, Room, TeachingAssignments, ClassRoomAssignments
WHERE Teacher.TeacherID = TeachingAssignments.TeacherID
AND Room.Building = ClassRoomAssignments.Building
AND Class.ClassID = TeachingAssignments.ClassID
AND Class.ClassID = ClassRoomAssignments.ClassID
Can anyone help? Thanks!
Your problem is that you need to add the respective joins for your table.
instead of doing:
SELECT Teacher.LastName, Class.ClassID, Room.Building
FROM Teacher, Class, Room, TeachingAssignments, ClassRoomAssignments
WHERE Teacher.TeacherID = TeachingAssignments.TeacherID
AND Room.Building = ClassRoomAssignments.Building
AND Class.ClassID = TeachingAssignments.ClassID
AND Class.ClassID = ClassRoomAssignments.ClassID
you need something like that
SELECT Teacher.LastName, Class.ClassID, Room.Building
FROM
Teacher INNER JOIN TeachingAssignments
ON Teacher.TeacherID = TeachingAssignments.TeacherID
INNER JOIN Class
ON Class.ClassID = TeachingAssignments.ClassID
INNER JOIN ClassRoomAssignments
ON Class.ClassID = ClassRoomAssignments.ClassID
INNER JOIN Room
ON Room.Building = ClassRoomAssignments.Building
As you can see every INNER Join is followed by it respective ON clause which is in charge of designing which element is going to be joined.