SQL Server query to know if an id in a column equals to other in another column - sql

I’ve been hours trying to build this query and I need your help so I can make it.
This is table Students (made out of inner joins):
SpecialtyChosenID StudentID Subject SubjectSpecialtyID
5ABFB416-8137 15 Math A1EBF3CB-E899
5ABFB416-8137 15 English A1EBF3CB-E899
The info in it means that a student with id no. 15 has chosen an specialty with id 5ABFB416-8137
The two subjects he has passed (Math and English) belong to a specialty with id A1EBF3CB-E899
What would be the query to know if the passed subjects belong to the specialty chosen by the student??
Counting the number of subjects with the same SubjectSpecialtyID as SpecialtyChosenID and vice versa could do.
Thanks a lot

You can do a self join. This finds the number of subjects taken by the student that match the student's chosen specialities.
SELECT l.SpecialtyChosenID, l.StudentID, Count(Distinct r.Subject) FROM Students l
LEFT JOIN Students r ON (l.StudentID=r.StudentID AND l.SpecialityChosenID=r.SubjectSpecialityID)
GROUP BY l.SpecialtyChosenID, l.StudentID
However, this is quite inefficient using the table structure given. If you have a table listing students with their specialities, and another with subjects and specialities, and a third relating students with subjects, it would be better to build this query from the base data, rather than from the derived data.

SELECT * FROM Students WHERE SpecialtyChosenID = SubjectSpecialtyID

If you only need the list of matching subjects and you have the SpecialtyChosenID you can do something like
SELECT * FROM Students
WHERE SubjectSpecialtyID = SpecialityChosenID

CASE WHEN SpecialtyChosenID = SubjectSpecialtyID THEN 1 ELSE 0 END AS specialty

Related

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.

sql query to count and display full name

So I am very confused by this query to display teachers' full name. I have used teacher_name ||' '|| teacher_lastname, but when I try to do so for the students, something happens. I am trying to get number of times a teacher had an appointment with a student and display teacher fullname and students full name. Below is my query :
select d.teacher_id ||' '|| d.teacher_lastname as Teacher, count (distinct c.student_id)
from teacher d inner join class t on t.teacher_id=d.teacher_id
inner join classRoom tp on tp.dest_id=t.dest_id
inner join classFl ta on tp.dest_id=ta.dest_id
inner join students c on c.student_id=ta.student_id
group by d.teacher_id,d.teacher_lastname
This is the output, but when I add
select d.teacher_id ||' '|| d.teacher_lastname as Teacher,
c.student_name||' '||c.student_lastname as Student,
count (distinct c.student_id)
and also add it to the group by d.teacher_id,d.teacher_lastname,c.customer_name,c.customer_lastname
it gives the following result
What is wrong? How can I fix it to have the same nr of counts for each teacher?
I am using oracle sql developer
I think you need to remove the DISTINCT from inside the COUNT. You say you want "nr of times a teacher had an appointment with a student" which I interpret to mean that if teacher T saw student S 9 times you want:
T S 9
If you keep DISTINCT, then grouping on student name and counting the number of unique student IDs will only ever produce a 1 unless two students have identical names. In essence then, by keeping the DISTINCT you are counting "the number of different students with name X that that teacher met with" and mostly this is 1 because "1 unique student named Hana Baker met with xxxx yyyy", "1 unique student with name Dream Kenise met with xxxx yyyy" ....
If you do have students with the same name but a different ID, then you should add student ID to the GROUP BY to provide distinction between the two students. You don't have to add it to the SELECT, but you'll struggle to tell them apart if you do. If you have two students both called S, but one has ID 1 (and he saw T 5 times) and the other has ID 2 (and she saw T 4 times) you'll get a result of:
T S 5
T S 4
You might want to add other columns to your select to better tell the difference between them
In your first query, using DISTINCT meant "the number of different students that sought a meeting with the teacher". Omitting DISTINCT in that query would have counted "the number of times the teacher had a meeting with any student"

why results of two queries are different?

select distinct ID, title, takes.course_id
from course join takes
on course.course_id = takes.course_id
where takes.course_id in
(select takes.course_id
from takes
where ID = '10204');
select ID, title, takes.course_id
from course join takes
on course.course_id = takes.course_id
where ID = '10204';
I want to query the course IDs and the titles of the courses that a student whose ID is 10204 takes. The first gives a result with 5000 rows which is incorrect. The second give a correct result. So what is wrong with the first?
The first query gives you data for all students that happen to take a course that 10204 also takes.
Essentially the first query can be read as "Find all courses and the students that take them, for any course that is also taken by student 10204". You can look at the first query as a 3 way join. The results of the subquery select takes.course_id from takes where ID = '10204' would be the "third" table.
Adding to the pile on since everyone seems to be offering bits and pieces, some of which are oddly irate...
The first query says "Give me information on the students and courses where the courses were also taken by student 10204"
The second query says "Give me information on the students and courses taken by student 10204"
You say you wanted to get the course IDs and Titles for the courses taken by the student 10204, so obviously the second query is the correct one. You don't care about other student's that have taken the same courses.
Perhaps, to put it into perspective, rewriting the first, and incorrect query will help:
select distinct ID, title, takes.course_id
from course
join takes
on course.course_id = takes.course_id
join takes as takes2
on takes.course_id = takes2.course_id
WHERE
takes2.ID = '10204');
Well that is could be because in the first query you are quering where the course_id in takes table is equal to a specific course_id in that table (WHICH CAN BE NOT UNIQUE)
and in the second query you are straightly querying where the course_id is equal to a unique ID in that table!
Thanks you guys. I think my problem is that I did not realize that other students can take the same courses with the student having ID 10204. That this why though the condition is to query only courses take by the students 10204, the results is all about the courses taken by both 10204 and other students.
Because takes.ID != course.ID. The first you use takes.ID in the where clause but the second you use course.ID

best performance database design to store students exams result

previously i thought that it's easy to store exams data in simple table like this :
id
subject_id
student_id
mark
date
but i noticed that it is very stupid idea cuz it makes database very huge
let's assume that we have school with 5000 students and there are 12 subjects for each students per month ... it means that our exams table will contain 5000*12*10 = 600000 row per year ,, ok what about if we decided to know student history from joining date to current date
.. i think it's very stupid and i think also there are optimum solutions to make another design to fit my needs ....
i need to store exams data in another lighter way
thank's in advance :)
Here is another way.
Table Student stores name, etc
Table Subject has subject names such as Reading, Writing and Arithmetic, and maybe a MinimumPassingMark.
Table Exam has ExamId, ExamDate, and SubjectID. It may also have PercentageOfOverallGrade
Table StudentExam has StudentID, ExamId, and mark.
Subjects and exams have a one to many relationship (one subject can have many exams, each exam has one subject. Students and exams have a many to many relationship.
So if you want to know if Johnny can read, it's as simple as.
select case
when sum(mark) * PercentageOfOverallGrade >= then 'pass' else 'no' end result
from Student s join StudentExam se on s.StudentId = se.StudentId
join Subject su on se.SubjectId = su.SubjectId
where s.name = 'Johnny'
and su.name = 'Reading'

SQL query number of students per school

I have a table of students and schools. How do I select the total students per school?
I'm sure this is a very simple query, however I'm not sure how to proceed on from this:
SELECT tblSchools.name
FROM tblStudentDetails
INNER JOIN tblSchools
ON tblStudentDetails.schoolId = tblSchools.id
Group by the school and use count() to count the students
SELECT s.name, count(d.id) as students_count
FROM tblSchools s
INNER JOIN tblStudentDetails d ON d.schoolId = s.id
GROUP BY s.name
I want to add on to the accepted answer as well. Working for a school district and continuously having to pull counts of students there are a few additional things to keep in mind. What students are you looking for?
Do you want active students, inactive students, or active and inactive students.
Do you want to include students that have been no showed (were going to come to your school, but ended up not coming for even a day, this is recorded in most student information systems.
Is the student attending multiple schools, in which case you want to exclude them from counting in their second or third school.
I've built the script with the idea of a normalized school district database, where things are broken out by school year, and enrollment.
Often a basic script for me looks a little like this.
SELECT s.SCHOOL_NAME, COUNT(stu.STUDENT_GU) AS STUDENT_COUNT
FROM STUDENT stu
JOIN STUDENT_YEAR sy ON sy.STUDENT_ID = stu.STUDENT_ID
JOIN SCHOOL_YEAR scy ON scy.SCHOOL_YEAR_ID = sy.SCHOOL_YEAR_ID
JOIN SCHOOL s ON s.SCHOOL_ID = scy.SCHOOL_ID
JOIN YEAR y ON y.YEAR_ID = sy.YEAR_ID
WHERE y.SCHOOL_YEAR = 2017
AND (sy.CONCURRENT IS NULL OR sy.CONCURRENT OR != 'Not Concurrent')
AND sy.ENTER_DATE IS NOT NULL
AND sy.STATUS IS NULL
GROUP BY s.SCHOOL_NAME
Because each school year is a new year, students, and schools usually have a table for the basic data that doesn't change. But also tables that are school year specific. In my example STUDENT_YEAR and SCHOOL_YEAR is where we get into the specifics regarding which kids we are actually getting, and where they currently are. I utilize the YEAR table to identify which school year I want to look at.
The STUDENT_YEAR table is where we store the students concurrency flag, enter date, and so within that table I can use in the WHERE clause a way to filter out Inactive Students, Concurrent Students, and ensure each student is counted only once.
If those year values aren't included, at least in my database, I would get all students ever enrolled for every year we've got stored.