DBMS SQL Database Query - mysql

How to view this?
Problem: The oldest student per sProgram.
Table name is Student.
Columns are:
sID, sLast, sFirst, sMI, sProgram, sGender, sAge
Sample data:
sID sLast sFirst sMI sProgram sGender sAge
-------------------------------------------------------
001 Right Mc D BSIT M 26
002 Michael John G BSIT M 22
002 Franco James D BSCPE M 20
003 Step Ren D BSECE M 22
I want to display the oldest student of BSIT
The desired output is :
001 , Right, Mc, D, BSIT, M, 26

If you just want oldest student of 'BSID', you can try this:
SELECT *
FROM Student
WHERE sProgram = 'BSIT'
ORDER BY sAge DESC
LIMIT 1
Or if you want oldest student of each sProgram, you can do it like this:
SELECT t1.*
FROM Student t1
JOIN (
SELECT sProgram, MAX(sAge) AS sAge FROM Student GROUP BY sProgram
) t2 ON t1.sProgram = t2.sProgram AND t1.sAge = t2.sAge
-- WHERE t1.sProgram = 'BSIT'

Related

SQL: elegant way to get first, second, and third degree associations

I have tables named course, student and students_in in a MySQL database. The tables look like this:
course
course_id name
3 Physics
12 English
19 Basket Weaving
4 Computer Science
212 Discrete Math
102 Biology
20 Chemistry
50 Robotics
7 Data Engineering
student
id name
2 Sally
1 Bob
17 Robert
9 Pierre
12 Sydney
41 James
22 William
5 Mary
3 Robert
92 Doris
6 Harry
students_in
course_id student_id grade
3 2 B
212 2 A
3 12 A
19 12 C
3 41 A
4 41 B
212 41 F
19 41 A
12 41 B
3 17 C
4 1 A
102 1 D
102 22 A
20 22 A
20 5 B
50 3 A
12 92 B
12 17 C
7 6 A
Here is a Fiddle: http://sqlfiddle.com/#!17/8d86ee/34
My goal is to get the id and name of the students who:
have taken a course with Sally (i.e. "first-degree" relationship), OR
have taken a course with someone who has taken a course with Sally (i.e. "second-degree" relationship), OR
have taken a course with someone who has taken a course with someone who has taken a course with Sally (i.e. "third-relationship" relationship)
Essentially, we're looking for first-, second-, and third-degree relationships to Sally.
Here is a depiction of what this looks like:
Since Sally took course IDs 3 and 212, the desired result would look like this (not the colorful table above, which I provided for illustration of the logic involved):
student_id student_name
12 Sydney <-- took course ID 3 with Sally
41 James <-- took course ID 3 and 212 with Sally
17 Robert <-- took course ID 3 with Sally
1 Bob <-- took course ID 4 with James
92 Doris <-- took course ID 12 with James and Robert
102 William <-- took course ID 102 with Bob
I tried to solve this problem by using a Common Table Expression (CTE) to query the first-degree relationships, and can probably use two additional CTEs to get the second-degree and third-degree relationships. But, this feels like a very inelegant way to do this.
Can someone please help with an elegant approach to this problem?
Thank you!
You can use a recursive cte:
with recursive cte(cid, sid, name, l) as (
select si.course_id, si.student_id, s.name, 1 from students_in si
join student s on s.id = si.student_id where si.course_id in (select si1.course_id
from students_in si1 join student s1 on s1.id = si1.student_id and s1.name = 'Sally') and s.name != 'Sally'
union all
select si.course_id, si.student_id, s.name, c.l + 1 from cte c
cross join students_in si
join student s on s.id = si.student_id
where si.course_id in (select si1.course_id
from students_in si1 where si1.student_id = c.sid) and si.course_id != c.cid and si.student_id != c.sid and c.l < 3
)
select distinct sid, name from cte where name != 'Sally'
See fiddle.
With Recursive coursemates As (
Select y.student_id, 1 as removal
From students_in x Inner Join students_in y
On x.course_id=y.course_id
Where x.student_id=2
UNION
Select y.student_id, r.removal+1
From coursemates r Inner Join students_in x
On r.student_id=x.student_id
Inner Join students_in y
On x.course_id=y.course_id
Where removal<=2
)
Select c.student_id, min(c.removal) as howfar, min(s.name) as student_name
From coursemates c Left Outer Join student s
On c.student_id=s.student_id
Where student_id <> 2
Group By c.student_id
Order by 2, 1
A little verbose, but also a little more generalized than your try in that you can control the depth.
A few defensive additions: 1. Left join on student table in case no R.I. there. 2. Filter out Sally from the result (don't care that Robert was with Sally and then Sally was with Robert)
Join's repeated as many times as needed, also good, but perhaps less elegant:)
with rel as ( --join student->student thru course
select t1.student_id s1Id, t2.student_id s2Id
from students_in t1 inner join students_in t2
on t2.course_id=t1.course_id
where t2.student_id<>t1.student_id
group by t1.student_id,t2.student_id
)
,four as(
select t1.s1Id as s1Id1 ,t2.s1Id s2Id1,t2.s2Id s2Id2 ,t3.s2Id s3Id2
from rel t1 left join rel t2 on t2.s1Id=t1.s2Id and t2.s2Id<>t1.s1Id
left join rel t3 on t3.s1Id=t2.s2Id and t2.s2Id<>t1.s1Id
and t3.s2Id<>t1.s1Id
where t1.s1Id=2
group by t1.s1Id ,t2.s1Id,t2.s2Id,t3.s2Id
)
select t1.s1Id1,t1,s3Id2,s1.name,s3.name,s4.name,s6.name
from four t1
inner join student s1 on t1.s1Id1=s1.id
inner join student s3 on t1.s2Id1=s3.id
inner join student s4 on t1.s2Id2=s4.id
inner join student s6 on t1.s3Id2=s6.id

show who resit and passed and what course was it?

i need help find who fail in exam & resit and pass the exam only,
heres the code:
select STUDENT_ID,EXAM_ID,SCORE,PASS_THRESHOLD,s.NAME , c.NAME as Course_name, EXAM_DT,
case
when SCORE>=PASS_THRESHOLD then 'PASS'
else 'Fail'
end as Flag
from exam_submission es
left join student s on es.STUDENT_ID = s.ID
left join exam e on es.EXAM_ID = e.ID
left join course c on e.COURSE_ID = c.ID
heres the result:
STUDENT_ID EXAM_ID SCORE PASS_THRESHOLD NAME Course_name EXAM_DT Flag
1 3 88 65 Anthony Data Mining 2019-12-17 PASS
1 5 71 70 Anthony Statistic 2019-12-19 PASS
2 1 53 55 Sisca Machine Learning2019-12-17 Fail
2 3 77 65 Sisca Data Mining 2019-12-17 PASS
2 4 85 63 Sisca Data Science 2019-12-18 PASS
2 1 60 55 Sisca Machine Learning2020-01-08 PASS
I need find like this:
2 1 53 55 Sisca Machine Learning2019-12-17 Fail
2 1 60 55 Sisca Machine Learning2020-01-08 PASS
Possibly using a query like below.
this is using your query as input.
Also we have assumed that it is not possible to have a student have (PASS, FAIL) for a student on same exam on two years chronologically.
; with inputdata as
(
select STUDENT_ID,EXAM_ID,SCORE,PASS_THRESHOLD,s.NAME , c.NAME as Course_name, EXAM_DT,
case
when SCORE>=PASS_THRESHOLD then 'PASS'
else 'Fail'
end as Flag
from exam_submission es
left join student s on es.STUDENT_ID = s.ID
left join exam e on es.EXAM_ID = e.ID
left join course c on e.COURSE_ID = c.ID
)
select * from Inputdata I
join
( select student_id, exam_id from
inputdata
group by student_id, exam_id
having count(distinct flag)=2
)T on I.student_id=T.student_id and I.exam_id=T.exam_id
order by exam_dt asc

Remove Duplicate Amounts

I have two tables with invoice detail and serial numbers.
Both tables have invoice number, but when I attempt to join using left or inner join the amount duplicates in each lines. I want to list down all the serials associated with my invoice # in table 1 without duplicating the amount each lines. I'm currently using MS Access.
Thanks for the help.
Table 1
Invoice# amount
001 500
Table 2
Invoice# serial
001 123
001 456
001 789
001 1011
001 1213
Desired Output:
Invoice# amount serial
001 500 123
456
789
1011
1213
My Current Query Output:
Query:
Select invoice.invoice,invoice.amount,tblmachine.serial
From tblmachine inner join invoice on tblmachine.invoice =invoice.invoice;
Use UNION ALL for 2 queries.
The 1st will return the 1st row that you need and the 2nd all the rest:
select i.[invoice#], i.amount, min(t.serial) as serial
from tblmachine as t inner join invoice as i
on t.[invoice#] = i.[invoice#]
where i.[invoice#] = '001'
group by i.[invoice#], i.amount
union all
select null, null, t.serial
from tblmachine as t
where t.[invoice#] = '001'
and t.serial > (select min(serial) from tblmachine where [invoice#] = t.[invoice#])
Results:
invoice# amount serial
001 500 123
456
789
1011
1213
Can't do that desired output in query for multiple invoices.
Build a report and set textbox HideDuplicates property to Yes.

MYSQL Get lowest value in column of a group specified by another column

I have a table that looks like this:
id name yearofstudy mark
1 Alain A 2 75
2 Michael B 3 85
3 Chen C 1 55
4 Caroline D 2 60
5 Mohamed E 2 60
6 Alex F 1 55
7 Sofia O 3 78
8 Samir O 1 85
9 Rob G 2 78
10 Big K 3 55
And I'm trying to get the id, name, year and mark of the students with the lowest (and highest) mark in each year which would give:
id name yearofstudy mark
3 Chen C 1 55
4 Caroline D 2 60
10 Big K 3 55
SQL isn't my strong point and I've been trying using the MIN() function but I haven't managed to get it right yet and would really appreciate some help.
Using a subquery to get the min() and max() for each yearofstudy, and joining it to the original table. (You did say you wanted lowest and highest, right?)
select t.id, t.name, t.yearofstudy, t.mark
from t
inner join (
select
yearofstudy
, min(mark) as minMark
, max(mar) as maxMark
from t
group by yearofstudy
) as m
on t.yearofstudy = m.yearofstudy
and (t.mark = minMark or t.mark = maxMark)
or for just the lowest mark per year:
select t.id, t.name, t.yearofstudy, t.mark
from t
inner join (
select
yearofstudy
, min(mark) as minMark
from t
group by yearofstudy
) as m
on t.yearofstudy = m.yearofstudy
and t.mark = minMark
You could write the query as follows:
SELECT t1.* from your_table t1
INNER JOIN (
SELECT yearofstudy, MIN(marks) as marks
FROM your_table GROUP BY yearofstudy
) t2
ON t1.yearofstudy = t2.yearofstudy
AND t1.marks = t2.marks
GROUP BY t1.yearofstudy
ORDER BY t1.yearofstudy, t1.id;
If all the MIN records for the yearofstudy are required, then you could simply remove GROUP BY t1.yearofstudy
Demo

Get total count and total absents in one query

I have 2 tables student and attendance. I need to join both tables and get the result like number of total absents and number of student in particular class and classname. I need a single query to get the below results
tbl_student:
admission_no(PK) student_name student_class
345 John X A
352 Sachin X A
322 Steve IX A
123 Pinky X A
343 Rose IX A
tbl_admission:
admission_no(FK) date_absent
354 2015-03-30
123 2015-03-30
322 2015-03-30
Result should be like this:
Date_absent total_absent total_students student_class
2015-03-30 2 3 X A
2015-03-30 1 2 IX A
you can do something like this to manage it in single query:
SELECT date_absent, total_absent, total_students, a1.student_class
FROM (
SELECT date_absent, count( tbl_admission.admission_no ) AS total_absent, tbl_student.student_class
FROM `tbl_admission`
JOIN `tbl_student` ON tbl_student.admission_no = tbl_admission.admission_no
GROUP BY tbl_student.student_class
)a1
JOIN (
SELECT count( tbl_student.admission_no ) AS total_students, tbl_student.student_class
FROM tbl_student
GROUP BY tbl_student.student_class
)a2
ON a1.student_class = a2.student_class
ORDER BY `a1`.`student_class` DESC