Counting Attendance Per Student - sql

Software: PowerSchool (Oracle database) Education software
When I run the sql for one student the attendance count in correct. When I add addition students the count becomes the total from all the students, and that total is listed for each student. How do I reset the count after each student or does the subquery go in a different location? Any suggestions appreciated. ~Liz
SELECT s.DCID,
s.lastfirst,
(SELECT COUNT(distinct att.ID)
FROM Students s JOIN Attendance att ON
s.id=att.studentid
JOIN Attendance_code attc ON
attc.id=att.attendance_codeID
WHERE s.id=att.studentid
AND s.id=5538
AND att.Att_Mode_Code='ATT_ModeDaily'
AND att.yearid=27
AND attc.Presence_Status_CD = ('Absent')) AS TAbs
FROM Students s
WHERE s.enroll_tatus=0
AND s.grade_level IN (6, 7, 8)
AND s.id=5538
ORDER BY s.grade_level ASC, s.lastfirst ASC;

Your query should be something like this:
SELECT s.DCID,
s.lastfirst,
(SELECT COUNT(distinct att.ID)
FROM Students s JOIN Attendance att ON
s.id=att.studentid
JOIN Attendance_code attc ON
attc.id=att.attendance_codeID
WHERE s.id=att.studentid
AND s.id=5538
AND att.Att_Mode_Code='ATT_ModeDaily'
AND att.yearid=27
AND attc.Presence_Status_CD = ('Absent')) AS TAbs
FROM Students s
WHERE s.enroll_tatus=0
AND s.grade_level IN (6, 7, 8)
AND s.id=5538
GROUP BY s.DCID, s.lastfirst #You should add this line
ORDER BY s.grade_level ASC, s.lastfirst ASC;

3 things you should change:
Remove both student's IDs filters. You want to see all students, not just the one with ID 5538.
Use different alias for the same table! You are referencing the Students table twice, and both times with the same alias (S).
Link the outmost Student table ID with the subquery's Student table ID, so each COUNT(DISTINCT att.ID) is calculated by student, and not overall.
Here is the edited query:
SELECT
s1.DCID,
s1.lastfirst,
(
SELECT
COUNT(distinct att.ID)
FROM
Students s2 -- Use different alias!
JOIN Attendance att ON s2.id=att.studentid
JOIN Attendance_code attc ON attc.id=att.attendance_codeID
WHERE
att.Att_Mode_Code='ATT_ModeDaily' AND
att.yearid=27 AND
attc.Presence_Status_CD = ('Absent') AND
s1.id = s2.id -- Relate both Student tables
) AS TAbs
FROM
Students s1
WHERE
s1.enroll_tatus=0 AND
s1.grade_level IN (6, 7, 8)
ORDER BY
s1.grade_level ASC,
s1.lastfirst ASC;

Related

Students have mentors, who are also students. How do I get the name of students that are mentoring more than 4 people?

The students table has 4 fields. Student ID, name, location, and mentor ID. The mentor ID points to a student ID. Students can mentor many people, however a person can only have one mentor.
The two methods I have come up with are as follows:
SELECT s.Name
FROM Students s
WHERE s.Id IN (SELECT s2.MentorId
FROM Students s2
GROUP BY s2.MentorId
HAVING COUNT(s2.MentorId) > 4)
OR
SELECT s2.Name
FROM students s
JOIN students s2 ON s2.Id = s.MentorId
GROUP BY s2.Name
HAVING COUNT(s2.Name) > 4
Are these correct? They do not run for me. I am unsure if this is an error with my SQL or my code.
Thanks!
No, they are not correct, but it's only a small, simple mistake. (same mistake in both).
You are counting the wrong column in the having clause.
Personally, I would go with the second option, only replace the COUNT(s2.Name) to COUNT(s.Id) - and if your database supports it, COUNT(DISTINCT s.Id) is even better:
SELECT s2.Name
FROM students s
JOIN students s2
ON s2.Id = s.MentorId
GROUP BY s2.Name
HAVING COUNT(DISTINCT s.Id) > 4
I would start with aggregation. The students who are mentoring more than 4 students are given by:
select s.mentorid
from students s
group by s.mentorid
having count(*) > 4;
To get the full student record, you can then use in, exists, or join:
select s.* -- or whatever columns you like
from students s join
(select s.mentorid
from students s
group by s.mentorid
having count(*) > 4
) m
on s.id = m.mentorid;
You were grouping by s2.name having a number of s2.name greater than 4. You never use the same field for grouping and the aggregate function. What you really want here is grouping by s2.name having a number of s.name (you want to count the corresponding students for each mentor mentor : GROUP BY mentor HAVING COUNT students > 4)
You should infact use s.id because otherwise it will not work if you have several students with the same name
SELECT s2.Name
FROM students s
JOIN students s2 ON s2.Id = s.MentorId
GROUP BY s2.Name
HAVING COUNT(s.id) > 4
Tested and it works :)

SQL query to get most recent row

I have a Student History table which maintains the enrolled section history for each student. For example, Student X is presently in Section 1 and Student X may have been in other sections in the past (including past enrollment in Section 1).
Each time Student X changes to another section a record is added to the Student History table.
The Student History table has following structure:
Student Id, Date_entered, section_id
I need to write a SQL query to get the records for the following scenario:
Get Student Id of all students CURRENTLY in Sections 1 & 2 (Students most recent date_entered must have been either Sections 1 or 2). The results should not include any students who were in these sections 1 & 2 in the past.
Sample Query:
select student_id from student_Queue_history where section_id in (1, 2)
Can someone help me write query for this one?
You can first select max date for each student and join it back to the student_history table.
with maxdate as (
select student_id, max(date_entered) as mxdate
from student_history
group by student_id)
select s.*
from student_history s
join maxdate m on s.student_id = m.student_id and s.date_entered = m.mxdate
where s.section_id in (1,2)
You have some pretty challenging design flaws with your table but you can leverage ROW_NUMBER for this. This is not the best from a performance perspective but the suboptimal design limits what you can do. Please realize this is still mostly a guess because you haven't provided much in the way of details here.
with CurrentStudents as
(
select *
, ROW_NUMBER() over(partition by student_id order by date_entered desc) as RowNum
from student_Queue_history
)
select *
from CurrentStudents
where section_id in (1, 2)
and RowNum = 1
select a.student_id
from student_Queue_history as a
where a.section_id in (1, 2)
and not exists (select b.student_id from student_Queue_history as b where b.student_id = a.student_id and b.Date_entered > a.Date_entered)

Writing two queries (sort students and cities according to average grades)

I have three tables:
1) Students: studentID (KEY), name, surname, address
2) Exams: examID (KEY), examName
3) Grades: studenID (KEY), examID(KEY), grade
How to write SQL query to show the best students (for example those with average grade above 9)?
How to write SQL query to rank Cities (column address) according to the average grade of their students?
I'm a system engineer, working with Unix and Linux systems and I am new in SQL, I only know about SQL basics, and I was trying to do this for past three days, with no success, so please help me. I presume it's not a complex thing for one who's experienced in SQL. Thanks a lot.
your first query to show the best students :
SELECT student.surname, students.surename, students.address
FROM
students INNER JOIN Grades ON Grades.StudentID=Students.StudentID
INNER JOIN Exams ON Grades.examID=exams.examID WHERE Grades.grade=
(SELECT MAX(grade) FROM Grades WHERE examID=exams.examID)
your second query to rank cities:
SELECT students.address
FROM
students INNER JOIN Grades ON Grades.StudentID=Students.StudentID
INNER JOIN Exams ON Grades.examID=exams.examID order by grades.grade DESC
Refer the fiddle here:
LINK 1 : http://sqlfiddle.com/#!4/ab4de6/19
LINK 2 : http://sqlfiddle.com/#!4/ab4de6/32
Below Queries should help you in Oracle:
--List of Students having Average grade >=9
SELECT S.studentID, S.NAME, S.SURNAME, S.ADDRESS, A.AVG_GRADE FROM
STUDENTS S JOIN
(
SELECT studentID, AVG(GRADE) AVG_GRADE FROM GRADES
GROUP BY studentID
) A
ON S.studentID = A.studentID
AND A.AVG_GRADE >=9
ORDER BY A.AVG_GRADE, S.studentID;
--------------------------------------------------------------------
--- Rank cities
SELECT A.ADDRESS, A.AVG_GRADE, ROWNUM RANKING FROM
(
SELECT S.ADDRESS, AVG(G.GRADE) AVG_GRADE FROM
STUDENTS S JOIN GRADES G
ON S.STUDENTID = G.STUDENTID
GROUP BY S.ADDRESS
ORDER BY 2 DESC
) A;
You need to know about the following concepts.
INNER QUERY / SUB QUERY
JOINS
AGGREGATE FUNCTIONS (Average Calculations)
GROUP BY
ORDER BY
ROWNUM

Adding 0 or null to missing fields

I have the following tables:
student(sid, sname)
teacher(tid, tname)
enrollment(sid, cid, tid)
course(cid, course)
rank(sid, tid, grade, date, valid)
I need to calculate the average grade of all the teachers (data is in rank table), when only the grade from the most recent date counts (and if it's invalid - ignore it).
I wrote the following query, and it's working nice. The problem is that I also need the average for ALL the teachers, including those who were not ranked yet/their rank is invalid (their average grade will be 0 in that case, and I'll have to count their students like I did for the others).
I think it's something with LEFT OUTER JOIN, but all the examples I see online have only two tables in FROM, and I can't figure out the right syntax in my case.
SELECT teacher.tid,
tname,
AVG(grade) AS avgGrade,
COUNT(DISTINCT enrollment.sid) AS studCount
FROM rank,
teacher,
enrollment,
( SELECT rank.sid, rank.tid, MAX(date) AS maxDate
FROM rank
GROUP BY sid, tid
) lastGrades
WHERE teacher.tid=enrollment.tid
AND rank.tid=teacher.tid
AND rank.tid=lastGrades.tid
AND rank.sid=lastGrades.sid
AND rank.date=lastGrades.maxDate
AND valid = TRUE
GROUP BY teacher.tid, tname
You could use a subquer to look up the latest rank per (teacher, student) combination. Use a left join to count enrollments that have not been ranked:
select t.tid
, t.tname
, avg(r.grade) as AverageRank
, count(distinct e.sid) as StudentCount
from teacher t
join enrollment e
on t.tid = e.tid
left join
rank r
on r.tid = t.tid
and r.sid = e.sid
and r.valid = true
and r.date =
(
select max(date)
from rank r2
where r2.sid = r.sid
and r2.tid = r.tid
and r2.valid = true
)
group by
t.tid
, t.tname
Example without data at SQL Fiddle.
The table design is kind of strange. You'd expect a student to enroll in a course, not in a teacher!

MYSQL get amount of results

When you use mysql, you can do something like this:
SELECT * FROM student WHERE teacher_id = 1
...then you get the amount of results, and with that the result. But I only want the amount of results but then for every teacher.
Is that possible in mysql like sizeof or something?
Also, if there are teachers that have no students is it true that there not in the list? or does it have to say 0?
Do you mean you want the number of students for every teacher? You can use something like this:
SELECT teacher_id, count(*) AS student_count
FROM student
GROUP BY teacher_id
This will not include teachers that don't have any students. To include them in the results, you need to use a JOIN (assuming you have a teacher table):
SELECT teacher.id, sum(if(student.id IS NULL, 0, 1)) AS student_count
FROM teacher
LEFT JOIN student ON student.teacher_id=teacher.id
GROUP BY teacher.id
SELECT teacher_id, count(*) as count FROM student GROUP BY teacher_id;
Use:
SELECT t.teacher_id,
IFNULL(ns.num_students, 0)
FROM TEACHERS t
LEFT JOIN (SELECT s.teacher_id,
COUNT(s.student_id) 'num_students'
FROM STUDENTS s
GROUP BY s.teacher_id) ns ON ns.teacher_id = t.teacher_id