Difficulty understanding a SQL Join query - sql

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

Related

Native Query to Select records to only contain specific ids

I am in the process of implementing an advanced search in my application. I'm sort of new with Oracle and JPA. The database structure has a many-to-many relationship (ERD) with an intermediate table that contains StudentID and CourseID.
I'm trying to return rows of students that have the list of classes/courses.
Basically, I want the results of
SELECT DISTINCT s.* FROM STUDENT s
INNER JOIN STUDENT_COURSE sc ON s.StudentID = sc.StudentID
INNER JOIN COURSE c ON c.CourseID = sc.CourseID
WHERE ( c.CourseID IN (SELECT DISTINCT CourseID FROM STUDENT_COURSE WHERE CourseID = 'A01') AND c.CourseID IN (SELECT DISTINCT CourseID FROM STUDENT_COURSE WHERE CourseID = 'A02'));
which returns the records of students that have both courses 'A01' and 'A02'.
ID
Age
Grade
1
...
...
4
...
....
9
...
....
with courses
Courses
A01,A02
A01,A02,A03,X02
A01, A02, A03
The goal is to get a similar result using Spring Data JPA. And make it more general to select any number of Course Ids.
I've tried
#Query(value="SELECT DISTINCT s.* FROM STUDENT s "
+"INNER JOIN STUDENT_COURSE sc ON s.StudentID = sc.StudentID "
+"INNER JOIN COURSE c ON c.CourseID = sc.CourseID "
+"WHERE ( s.STUDENTID IN (SELECT DISTINCT STUDENTID FROM STUDENT_COURSE WHERE CourseID =:courseIds)",nativeQuery=true))
public List<Student> advancedSearch(#Param("courseIds") String courseIds);
One example, The courseIds field contain "A01,A02". The result would be empty.
I've looked at examples where people use IN. When I tried it, it would return records of the student have courses AO1 OR A02 OR Both.
DON'T WANT THIS
ID
Age
Grade
2
...
...
3
...
....
8
...
....
With Courses
CourseID
A01,A03
A01, X01
A02
I want records of students that have both A01 AND A02 as shown in the other table.
My guess is that the query you want is
select s.name /* note this column doesn't appear in your ERD */
,s.age
,s.grade
from student s
join student_course sc
on s.studentID = sc.studentID
where sc.courseID IN ('A01', 'A02')
group by s.name /* again, this column isn't in your ERD */
,s.age
,s.grade
having count(sc.courseID) = 2
That doesn't produce the output table you say you want. But neither does the query you posted. This will return all the students that have taken both courses (you could add a distinct to the having clause if the data model were to change to allow a student to take a course multiple times). It won't give you the list of all courses that student has taken as a comma-separated list. But the query you posted doesn't return any course information either.
If this isn't what you are looking for, it would be really helpful to update your question with
The DDL for your schema in text form (so we can run it) rather than as an image
The DML to insert whatever sample data you want
The actual results you'd want the query to return (since there is a conflict between what the query you posted returns, the table of data you say you want, and the descriptive text of what you are trying to accomplish, it is confusing to try to figure out which is right).

SQL for returning rows against only one type

I was not sure what to put in the title. I have a
SCHOOL table, with:
id,
name,
type_id
columns.
A SCHOOL_STUDENT table with:
school_id,
student_id
columns
and a SCHOOL_TYPE table with
id,
name
columns.
Students can be enrolled in more than one schools. A school can be of only one type. Given a student I want to check if he/she is enrolled in schools with only type(high shcool), and no other schools. I want to check if the student is enrolled in schools with only type(high shcool), and no other schools. If a student is enrolled in two schools and one of them is a high school and one of them is a technology school then I want some negative indication. If both of those schools are high school , i want positive indication. Let's suppose SCHOOL_TYPE has a row with id:3 and name:"high school".
SAMPLE DATA:
SCHOOL table
id:name:type_id
1:'abc school':3
2:'xyz school':4
3:'HEH school':3
SCHOOL_TYPE table
id:name
1:'junior school'
2:'tech school'
3:'High school'
SCHOOL_STUDENT table:
school_id:student_id
1,123
3,123
2, 56
3, 56
See studentID:123 is enrolled in two schools, with id:1 and id:3. And both of those schools are of type:3, that means I want a success result. If the same student is enrolled in more than one school and those schools belong to other types as well (other than typeID:3) then I want a failure.
Expected output for studentID:123 is YES and for studentID:56 is NO because he is enrolled in two schools and one of them is not a high school.
I am trying this but I know it lacks something but what it lacks, I don't seem to work it out.
select
bc.customer_id, bt.name, count(bt.[name])
from
school_type st
join school s on s.type_id = st.id
join school_student ss on ss.school_id = s.id
where
ss.student_id = 1234
group by
ss.student_id, st.name
May be use HAVING? or CASE?
QUESTION UPDATED:
You can use correlated subquery with not exists
select
ss.student_id, st.name, count(bt.[name])
from
school_type st join school s on s.type_id = st.id
join school_student ss on ss.school_id = s.id
where
ss.student_id = 1234 and not exists (select 1 from school s1 where ss.school_id = s1.id and s1.type_id<>3)
group by
ss.student_id, st.name

Multiple joins onto same table

i have the following tables:
TABLE: teachers:
teacherID
teacherName
TABLE: students:
studentID
studentName
teacherID
advisorID
so, usually, i know i can get a single row per student, with their teachers name using an INNER JOIN.
but in this case - the advisor and tacher - are from the same teachers table. so how can i join onto the teachers table twice - once getting the teacher name, and then again to get the advisor name?
hope this is clear
thanks!
This lists students with the names of their teachers and advisors if any, in alpha order of student, without either (a) the teacher or (b) the advisor having to exist. If you want only where those names exist, change the respective join to an INNER join.
SELECT s.studentname as [Student], t.teachername as [Teacher], a.teachername as [Advisor]
FROM Students s
LEFT JOIN Teachers t ON s.TeacherID = t.TeacherID
LEFT JOIN Teachers a ON s.AdvisorID = a.TeacherID
ORDER BY 1, 2
You can join to the same table more than once, just give it a different alias for each join, and name your fields in a descriptive enough way. Use a left join if there might not be a link, but if a student always has both a teacher and an advisor, a straight join should be fine.
Something like this:
select s.studentname student
, t.teachername teacher
, a.teachername advisor
from students s
join teacher t
on t.teacherID = s.teacherID
join teacher a
on a.teacherID = s.teacherID
Why not try something like the following. Its been a while since I've done SQL so this may not work.
SELECT s.studentName AS Student, t.teacherName AS Teacher, a.teacherName AS Advisor
FROM teachers t, teachers a, students s
WHERE t.teacherID = s.teacherID AND a.teacherID = s.advisorID

SQL to select a table through a generic relationship

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'

what should be the relation ship between both the tables so each student can have separate marksid

i am new to sql
I have 2 tables first one with marks and second one with students.
marks have a primary key of marksid and students have primary key student id.
I want to know what should be the relation ship between both the tables so each student can have separate marksid.
and what will be the sql query to get their data.
I have tried one to one relationship with this select query
s="select s.Student Name,z.Hourly1Marks from Student s,marks z "
but it is showing Cartesian product of entries.
Study the concept of INNER JOIN (if each student has one mark, i.e. 1:1 relationship), and LEFT JOIN (if each student can have multiple marks, i.e. 1:n relationship).
You used an inner join without a restriction. What you need is something like
SELECT S.name, Z.marks
FROM students S
JOIN marks Z ON S.student_id = Z.student_id
or equivalently
SELECT S.name, Z.marks
FROM students S, marks Z
WHERE S.student_id = Z.student_id
for a 1:1 relationship case, or
SELECT S.name, Z.marks
FROM students S
LEFT JOIN marks Z ON S.student_id = Z.student_id
for a 1:N relationship case.
Supposing you have two tables:
Students
--------------
IDStudent
Name
....
Marks
--------------
IDMarks
Hourly1Marks
and the relation between Studend and Marks is 'A student could have 0 or more Marks' then you need to add a column in the table Marks - call it studentID and this column is used as foreign key for the student table.
then you can write this query
select s.Name, z.Hourly1Marks
from Student s left join marks z
on z.studentID = s.IDStudent
that will give you back all students and their marks (also the student without any marks)
or, if you want only the students with at least one mark
select s.Name, z.Hourly1Marks
from Student s inner join marks z
on z.studentID = s.IDStudent