MySQL query the latest duplicate - mysql

EDIT:
I'm trying to do a MySQL query which will give me the latest entry for duplicates together with those without any duplicates.
This is what my table look like:
mentor table:
mentor_id applicant_id mentor_count mento_email mentor_name mentor_pass
193 92 1 test#yahoo.com test 1234
194 92 2 table#yahoo.com table 4567
195 92 3 lamp#yahoo.com lamp 7890
196 92 1 test#yahoo.com test 1234
197 92 2 table#yahoo.com table 4567
198 92 3 lamp#yahoo.com lamp 7890
mf table:
mf_id mentor_id dept contact orgname yrs length sak social char goal resp emomat res others impact evaluation
43 193 math dept 9111111 etc 1 1 e e e e e e e e e good
114 196 math dept 9111111 etc 1 1 e e e e e e e e e good
193 197 sci dept 9222222 org 2 2 n n n n n n n n n medium
194 194 sci dept 9222222 org 2 2 n n n n n n n n n medium
220 195 eng dept 9333333 hello 3 3 q q q q q q q q q bad
I tried using this query:
SELECT *
FROM mentor m1
LEFT JOIN (
SELECT mentor_name, max( mentor_id ) AS maxid
FROM mentor m
GROUP BY m.mentor_id
)m2 ON m1.mentor_name = m2.mentor_name
AND m1.mentor_id < m2.maxid
LEFT JOIN mf ON m1.mentor_id = mf.mentor_id
WHERE m1.applicant_id =833
AND m2.maxid IS NULL
ORDER BY m1.mentor_id ASC
LIMIT 0 , 30
but this is what happens:
mentor_id applicant_id mentor_count mentor_email mentor_name mentor_pass mentor_name maxid mf_id mentor_id dept contact orgname yrs length sak social char goal resp emomat res others spirit concept comm impact evaluation
/*there is data here but the column for mentor_name onwards is null*/
How can I make it so that the columns for mentor_name onwards is not null, but still displays the latest duplicates as well as those without any duplicates?

try
select * from mentor
where mentor_id in
(
SELECT max(mentor_id) from mf
where applicant_id = 92
group by mentor_id
)

I guess you want to add mentor.application_id = mf.application_id to the JOIN condition
select *
from mentor
inner join
(
SELECT *, max(mentor_id) as maxid
from mf
group by mentor_id
) mf on mentor.mentor_id = mf.maxid AND mentor.application_id = mf.application_id
where applicant_id = 92
Typically you will need an extra condition to get the duplicates. WHERE applicant_id = 92 won't be a duplicate unless there were others with the same applicant_id in the same table.

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

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

rank on mysql with join

i need to display rank with my sql on 2 joined table, there's my mysql query to display
SELECT a.UserID, b.Nama,a.Matematika,a.IPA,a.IPS,a.BIND,a.BING,a.Rata,
FIND_IN_SET( a.Rata, (SELECT GROUP_CONCAT( a.Rata ORDER BY a.Rata DESC ) FROM datanilaiujian )) AS rank
from datanilaiujian as a JOIN
datauser as b
ON a.UserID=b.UserID
ORDER BY a.Rata DESC
but when i execute this command, mysql return Error Code: 1242. Subquery returns more than 1 row
list field on table datanilaiujian
UserID Matematika IPA IPS BIND BING Rata
1000 90 76 78.9 78 65 77.58
1001 78.9 87 67 56 78 73.38
1002 80 78.9 67 55 65.9 69.36
1003 78.9 56 77 88 90 77.97999999999999
list field on table datauser
UserID Pass Nama Alamat NoTelepon AsalSekolah Tanggal Masuk NilaiUN
1000 1000 Habib Jl.sesama 232323232323 23dsdsdsfsdfsdfsdfsdff 2017-01-13 19:35:22 Sudah
1001 1001 wisnu jl sesama 085600336706 SMA 2 Purwokerto 2017-01-28 17:35:32 Sudah
1002 1002 Arif Jl Sungkio 085600336706 SMA BINTEK 2017-01-28 19:30:56 Sudah
1003 1003 Akbar Jl sesama 085600133558 SMPN 1 Purwokerto 2017-02-02 18:59:47 Sudah
my expected result :
Nama Matematika IPA IPS BIND BING Rata Rank
I see the problem. Your subquery is saying a.Rata. This refers to the outer table. I suspect that the GROUP_CONCAT() is then confusing MySQL, so the subquery is not interpreted as an aggregation query.
However, you should move the subquery to the FROM clause:
SELECT a.*,
FIND_IN_SET( a.Rata, l.list) AS rank
from datanilaiujian a JOIN
datauser b
ON a.UserID = b.UserID CROSS JOIN
(SELECT GROUP_CONCAT( a2.Rata ORDER BY a2.Rata DESC ) as list
FROM datanilaiujian a2
) l
ORDER BY a.Rata DESC;
This generally helps the optimizer choose the best execution plan.

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