SQL query using NULL - sql

I have two tables, student and school.
student
stid | stname | schid | status
school
schid | schname
Status can be many things for temporary students, but NULL for permanent students.
How do I list names of schools which has no temporary students?

Using Conditional Aggregate you can count the number of permanent student in each school.
If total count of a school is same as the conditional count of a school then the school does not have any temporary students.
Using JOIN
SELECT sc.schid,
sc.schname
FROM student s
JOIN school sc
ON s.schid = sc.schid
GROUP BY sc.schid,
sc.schname
HAVING( CASE WHEN status IS NULL THEN 1 END ) = Count(*)
Another way using EXISTS
SELECT sc.schid,
sc.schname
FROM school sc
WHERE EXISTS (SELECT 1
FROM student s
WHERE s.schid = sc.schid
HAVING( CASE WHEN status IS NULL THEN 1 END ) = Count(*))

You can use not exists to only select schools that do not have temporary students:
select * from school s
where not exists (
select 1 from student s2
where s2.schid = s.schid
and s2.status is not null
)

You can use a regular join.
SELECT DISTINCT c.schName
FROM Students s
INNER JOIN Schools c ON s.schid = c.schid
WHERE s.status IS NULL

Related

Tricky statistics select

Trying to figure out this for a while, but no luck. I have the following tables (MS-SQL 2008):
students
studentID – email – profileID
courses
courseID – name
studentsCourses
studentID – courseID
profiles
profileID – name
profilesMandatoryCourses
profileID - courseID
studentsCoursesLogs
logID - studentID –courseID – accessDate
Each student, when enrolls, is assigned a profile. For each profile there are a number of mandatory course. Those mandatory courses together with any other courses a user takes are saved in the studentsCourses table.
Whenever a student accesses a course the information is logged in the studentsCoursesLogs.
I am trying to figure out all the students that have taken all the mandatory courses based on their profile.
Any pointers appreciated.
I think this should do it (assuming I've understood your data model and requirements correctly - and that in throwing this sample SQL together I've not made any mistakes - I didn't create your data model in a database). I'm pretty sure it should be close enough though.
select studentRecords.StudentId,
sum(case TakenCourseID when null then 0 else 1 end) as CompleteMandatoryCourses,
sum(case TakenCourseID when null then 1 else 0 end) as IncompleteMandatoryCourses
from (
select mandatoryCourses.StudentID, mandatoryCourses.CourseId as MandatoryCourseID, takenCourses.CourseID as TakenCourseID
from ( -- Courses student should have taken - based on their profile
select p.[Profile], pmc.CourseID, s.StudentID
from profiles p
inner join profilesMandatoryCourses pmc on p.ProfileID = pmc.Profile
inner join students s on p.StudentID = s.StudentID
) mandatoryCourses
left join
(
-- Course students have taken
select s.StudentID, s.ProfileID, sc.CourseID
from students s
inner join studentsCourseLogs scl on s.StudentID = scl.StudentID
) takenCourses on mandatoryCourses.ProfileId = takenCourses.ProfileID
and mandatoryCourses.CourseID = takenCourses.CourseID
) studentRecords
group by mandatoryCourse.StudentId
having sum(case TakenCourseID when null then 1 else 0 end) = 0
It will give a recordset like the following....
+----------------+-----------------------------+-------------------------------+
| StudentID | CompleteMandatoryCourses | IncompleteMandatoryCourses |
+----------------+-----------------------------+-------------------------------+
| 1 | 15 | 3 |
| 2 | 8 | 0 |
+----------------+-----------------------------+-------------------------------+
If you just want a list of students who have taken all mandatory courses, you could wrap all of the above as shown here...
select studentID
from ( /* insert very long sql here */ )
where IncompleteMandatoryCourses = 0`

SQL: tricky join

I'm building software for exam signup and grading:
I need to get data from these two tables:
Exams
|------------------------------------------------------------|
| ExamId | ExamTitle | EducationId | ExamDate |
|------------------------------------------------------------|
ExamAttempts
|-----------------------------------------------------------------------------|
| ExamAttemptId | ExamId | StudentId | Grade | NotPresentCode |
|-----------------------------------------------------------------------------|
Students attends an education
Educations have multiple exams
Students have up to 6 attempts per exam
Every attempt is graded or marked as not present
Students can sign up for an exam if:
- not passed yet (grade below 2)
- has not used all attempts
I want to list every exams that a student can sign up for.
It maybe fairly simple, but I just can't get my head around it and now I'm stuck! I've tried EVERYTHING but haven't got it right yet. This is one of the more hopeless tries I made (!):
CREATE PROCEDURE getExamsOpenForSignUp
#EducationId int,
#StudentId int
AS
SELECT ex.*
FROM Exams ex
LEFT JOIN (
SELECT ExamId, COUNT(ExamId) AS NumAttempts
FROM ExamAttempts
WHERE StudentId = #StudentId AND grade < 2 OR grade IS NULL
GROUP BY ExamId
) exGrouped ON ex.ExamId = exGrouped.ExamId
WHERE educationid = #EducationId and exGrouped.ExamId IS NULL OR exGrouped.NumAttempts < 6;
GO
What am i doing wrong? Please help...
You need to start with a list of all possibilities of exams and students and then weed out the ones that don't meet the requirements.
select driver.StudentId, driver.ExamId
from (select #StudentId as StudentId, e.ExamId
from exams e
where e.EducationId = #EducationId
) driver left outer join
(select ea.ExamId, ea.StudentId
from ExamAttempts ea
group by ea.ExamId, ea.StudentId
having max(grade) >= 2 or -- passed
count(*) >= 6
) NotEligible
on driver.ExamId = NotEligible.ExamId and
driver.StudentId = NotEligible.StudentId
where NotEligible.ExamId is NULL
The structure of this query is quite specific. The driver table contains all possible combinations. In this case, you have only one student and all exams are in the "education". Then the left join determines which are not eligible, based on your two requirements. The final where is selecting the non-matches to the not-eligible -- or the exams that are eligible.
Check if this works in your SP:
Select EduExams.ExamId
from
(select * from Exams
where Exams.EducationId = #EducationId) EduExams
left outer join
(select * from ExamAttempts
where ExamAttempts.StudentId = #StudentId) StudentAttempts
on EduExams.ExamID = StudentAttempts.ExamId
group by EduExams.ExamId
having count(StudentAttempts.ExamAttemptId) < 6
and ((max(StudentAttempts.Grade) is null) or (max(StudentAttempts.Grade) < 2))
OK thank you both for your help - much appreciated!
Based on #Gordon Linoffs solution. this is what I ended up with:
SELECT driver.ExamId, driver.ExamTitle
FROM (
SELECT #StudentId AS StudentId, e.ExamId, e.ExamTitle
FROM exams e
WHERE e.EducationId = #EducationId
) driver
LEFT JOIN (
SELECT ea.ExamId, ea.StudentId
FROM ExamAttempts ea
WHERE ea.studentId = #StudentId
GROUP BY ea.ExamId, ea.StudentId
HAVING MAX(grade) >= 2 OR COUNT(*) >= 6
) NotEligible
ON driver.ExamId = NotEligible.ExamId AND driver.StudentId = NotEligible.StudentId
WHERE NotEligible.ExamId IS NULL

Sql Query Join in Oracle

I have Parent table and multiple child tables with foreign key constraint.
School Table
School ID EduDetails Genders Address_id EDUTYPE
1 2 M 3 FGN
And the child tables like
Education Details
EDU ID EducationType
2 Online
AKA Name
School Id AKA Name
1 Test School
1 School Test
Gender Table
Gender ID Gender Desc
M Male
I am using Left outer join for the parent and school table to fetch the results.
But My issue is, If AKA table has 5 counts matching the school Id and Gender table has only 1 records for that school Id.
So the results comes with 5 duplicate rows with school Information and also other child table information.
Is there any workaround to fix this issue. I tried using subquery and row_number over by function. But it is not working for me. Can anybody help me to solve this issue.
Thanks in advance for your time in looking this issue.
My required output should be like this
School_id AKA Name GenderDesc EductaionType
1 Test School Male Online
1 School Test
So I need to have Null values for the not matching records.
Since you want all the records in the AKA Name table, I've joined on that getting a Row_Number for each row. Then using that Row_Number, LEFT JOIN on the other tables.
SELECT S.SchoolId,
SA.AKAName,
G.GenderName,
ED.EducationType
FROM School s
JOIN
(SELECT SchoolId,
AKAName,
ROW_NUMBER() OVER (PARTITION BY SchoolId ORDER BY AKAName) rn
FROM SchoolAKA
) SA ON S.SchoolID = SA.SchoolId
LEFT JOIN
(SELECT EDUID,
EducationType,
ROW_NUMBER() OVER (ORDER BY EducationType) rn
FROM EduDetails
) ED ON S.EDUID = ED.EDUID AND SA.rn = ED.rn
LEFT JOIN
(SELECT GenderId,
GenderName,
ROW_NUMBER() OVER (ORDER BY GenderName) rn
FROM Genders
) G ON S.GenderId = G.GenderId AND SA.rn = G.rn
Here is the SQL Fiddle.
And here are the results:
SCHOOLID AKANAME GENDERNAME EDUCATIONTYPE
1 School Test Male Online
1 Test School (null) (null)

Help with query

I'm trying to make a query that looks at a single table to see if a student is in a team called CMHT and in a medic team - if they are I don't want to see the result.
I only want see the record if they're only in CMHT or medic, not both.
Would the right direction be using sub query to filter it out? I've done a search on NOT IN but how could you get to see check if its in more then 2 teams are not?
Student Team ref
1 CMHT 1
1 Medic 2
2 Medic 3 this would be in the result
3 CMHT 5 this would be in the result
So far I've done the following code would I need use a sub query or do a self join and filter it that way?
SELECT Table1.Student, Table1.Team, Table1.refnumber
FROM Table1
WHERE (((Table1.Team) In ('Medics','CMHT'))
This is Mark Byers's answer with a HAVING clause instead of a subquery:
SELECT Student, Team, ref
FROM Table1
GROUP BY Student
HAVING COUNT(Student) = 1
SELECT *
FROM students
WHERE NOT EXISTS
(
SELECT NULL
FROM students si
WHERE si.student = s.student
AND si.team = 'CMHT'
)
OR NOT EXISTS
(
SELECT NULL
FROM students si
WHERE si.student = s.student
AND si.team = 'Medic'
)
SELECT a.*
FROM Table1 a
INNER JOIN
( SELECT Student, COUNT(*) FROM Table1
GROUP BY Student
HAVING COUNT(*) = 1)b
ON (a.Student = b.Student)
how could you get to see check if its in 2 or more teams?
You can count the number of teams per student and then filter only those you want to see:
SELECT student FROM
(
SELECT student, COUNT(*) AS cnt
FROM Table1
GROUP BY student
) T1
WHERE cnt = 1
You can do it with outer join
select COALESCE(t1.Student, t2.Student) as Student,
COALESCE(t1.Team, t2.Team) as Team,
COALESCE(t1.ref, t2.ref) as ref
from
(select * from Student where Team = 'CMHT') t1
outer join
(select * from Student where Team = 'Medic') t2
on t1.Student = t2.Student
where
t1.Student is null or
t2.Student is null;

SQL issue,challenge

Say we have two entities:teacher and student.
each teacher has multiple student.
Now I want to:
query for at most 5 teachers,and for each teacher, no more than 10 of his student.
So far this can be done quite easily by:
select *,
(
select GROUP_CONCAT('<sid>',students.name,'</sid>') from students on
teachers.id=students.teacher limit 10
) as students
from teachers limit 5
But that's not the whole story yet.
AND
If anyone of the teachers has more than 10 students,should return true for that teacher,otherwise false
How to do this in SQL?
Pseudo SQL: one row of results for each of the 10 students for each of the 5 teachers
select t.teacher_id, s.student_id,
case when t2.count > 10 then 'true' else 'false' end
from
(select top 5 *
from teachers
order by teacher_id) t
join
(select top 10 *
from students s1 join teachers t1 on s1.teacher_id = t1.teacher_id
order by student_id) s
on t.teacher_id = s.teacher_id
join
(select teacher_id, count(*) as count
from teachers t join students s on t.teacher_id = s.teacher_id
group by teacher_id) t2
on t2.teacher_id = t.teacher_id
LINQ makes this super-easy
Use a subquery for every teacher selected.