Convert query with exists to joins - mysql

I want to convert this query in such a way so it does not have any 'exists' and uses only simple joins.
select t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID
from Grades a
left join students s on a.student_ID = s.student_ID
left join Teachers t on t.Teacher_ID = s.Teacher_ID
where 1=1 and t.Teacher_id = 1807600
and exists(
select p.Payment_ID from payments p
inner join lookups l on (l.lookup_id = p.status_id and l.lookup_key in ('condition1','condition2'))
where p.student_ID = a.student_ID
)
I tried something like:
select t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID
from Grades a
left join students s on a.student_ID = s.student_ID
left join Teachers t on t.Teacher_ID = s.Teacher_ID
inner join payments p on p.student_ID = a.student_ID
inner join lookups l on (l.lookup_id = p.status_id and l.lookup_key in ('condition1','condition2'))
where 1=1 and t.Teacher_id = 1807600
But I'm not getting the right results. Can you please help. Thanks.

I suppose you could use group by, but this restricts the maintainability of the script for the simple sake of not using EXISTS().
select t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID
from Grades a
left join students s on a.student_ID = s.student_ID
left join Teachers t on t.Teacher_ID = s.Teacher_ID
inner join payments p on p.student_ID = a.student_ID
inner join lookups l on (l.lookup_id = p.status_id and l.lookup_key in ('condition1','condition2'))
where t.Teacher_id = 1807600
group by t.Teacher_id, t.Teacher_name, a.marks, a.grade_ID

Related

Given the following schema, what would be the query to print all students in a teachers class?

Schema:
This is what I'm thinking, but I don't think its right:
SELECT distinct t.teacherid, s.fname
FROM tea_cou t, students s, stu_cou c
WHERE t.courseid = c.courseid
You have to join the tables and then filter by teacher name.
SELECT S.*
FROM Teachers T
INNER JOIN Tea_Cou TC ON Tc.TeacherId = T.TeacherId
INNER JOIN Courses C ON C.CourseId= TC.CourseId
INNER JOIN Stu_Cou SC ON SC.CourseId = C.CourseId
INNER JOIN Students S ON S.StudentId = SC.StudentId
WHERE T.LNAME= 'name'

MySQL Multiple JOIN with most resent timestamp from one

I have problem that I hope someone can help me with.
SELECT a.country_name, s.state_name, c.city_id,
LEAST (c.next_1, c.next_2, c.next_3) AS next_visit,
MAX(v.visit_time) AS last_visit
FROM city c
INNER JOIN country a ON a.id = c.country
INNER JOIN state s ON s.id = c.state
INNER JOIN visit_log v ON CONCAT(c.country, c.state, c.city_id) = CONCAT(v.country, v.state, v.city_id)
GROUP BY CONCAT(v.country, v.state, v.city_id)
ORDER BY a.id ASC, s.id ASC, c.city_id
My main problem now is that I can't get the col_1 and col_2 from the visit_log corresponding with MAX(visit_log)
SQLfiddle
You can add the "latest" requirement to the join condition:
SELECT *
FROM city c
JOIN country a
ON a.id = c.country
JOIN state s
ON s.id = c.state
JOIN visit_log v
ON v.country = c.country
AND v.state = c.state
AND v.city_id = c.city_id
AND visit_time =
(
SELECT MAX(visit_time)
FROM visit_log v2
WHERE v2.country = c.country
AND v2.state = c.state
AND v2.city_id = c.city_id
)
You can find many other approaches in the greatest-n-per-group+mysql tag. For optimal speed you'd use an approach using variables.
You can try this:-
SELECT C.NAME, S.NAME, CN.ID, NV.Next_visit_1, VL.visited
FROM COUNTRY C INNER JOIN next_visit NV ON C.ID = NV.Country
INNER JOIN STATE S ON NV.State = S.ID
JOIN CITY
INNER JOIN visitor_log VL ON CONCAT(NV.country, NV.state, NV.city) = CONCAT(VL.country, VL.state, VL.city)

SQL query to select attendance per student using sub query cant use row from parent query

I have a problem in SQL, I'm trying to work out the student attendance for a class in a register system I'm building however it won't let me use the parent column in the sub query,
SELECT A.student_id, TRUNCATE((100 - ((100/B.reg_num) * C.abs_num)), 0) AS attendance FROM
students A
JOIN (
SELECT COUNT(*) AS reg_num
FROM students
JOIN seminargroup_student ON seminargroup_student.student_id = students.student_id
JOIN seminar_group ON seminar_group.seminar_group_id = seminargroup_student.seminar_group_id
JOIN modules ON modules.module_id = seminar_group.seminar_group_module_id
JOIN register_seminar ON register_seminar.seminar_id = seminar_group.seminar_group_id
JOIN registers ON registers.register_id = register_seminar.register_id
WHERE modules.module_id =1 AND students.student_id = A.student_id
) B
JOIN (
SELECT COUNT(*) AS abs_num
FROM students
JOIN seminargroup_student ON seminargroup_student.student_id = students.student_id
JOIN seminar_group ON seminar_group.seminar_group_id = seminargroup_student.seminar_group_id
JOIN modules ON modules.module_id = seminar_group.seminar_group_module_id
JOIN absence ON students.student_id = absence.student_id
WHERE modules.module_id =1 AND students.student_id = A.student_id
) C
This returns: #1054 - Unknown column 'A.student_id' in 'where clause'
Thanks for any help!
it won't let me use the parent column in the sub query
Generally speaking you don't need to reference the parent in a subquery that's in a FROM.
Instead you just add your joining fields to your SELECT and GROUP BY clauses in the subqueries and then join
e.g.
SELECT students.student_id,
Truncate(( 100 - ( ( 100 / b.reg_num ) * c.abs_num ) ), 0) AS attendance
FROM students
JOIN (SELECT a.studentid,
Count(*) AS reg_num
FROM students A
JOIN seminargroup_student
ON seminargroup_student.student_id = A.student_id
JOIN seminar_group
ON seminar_group.seminar_group_id =
seminargroup_student.seminar_group_id
JOIN modules
ON modules.module_id =
seminar_group.seminar_group_module_id
JOIN register_seminar
ON register_seminar.seminar_id =
seminar_group.seminar_group_id
JOIN registers
ON registers.register_id = register_seminar.register_id
GROUP BY a.studentid) A
ON students.studentid = a.student.id
JOIN (SELECT a.studentid,
Count(*) AS abs_num
FROM students aA
JOIN seminargroup_student
ON seminargroup_student.student_id = a.student_id
JOIN seminar_group
ON seminar_group.seminar_group_id =
seminargroup_student.seminar_group_id
JOIN modules
ON modules.module_id =
seminar_group.seminar_group_module_id
JOIN absence
ON a.student_id = absence.student_id
GROUP BY a.studentid) b
ON students.studentid = b.student.id
As a side note you don't have to do two subqueries if you use a left joins and do a DISTINCT COUNT on the PK fields instead of on *
SELECT
A.student_id,
TRUNCATE((100 - ((100/counts.reg_num) * counts.abs_num)), 0) AS attendance
FROM
students A
JOIN
(SELECT
COUNT(DISTINCT absence.absence_id) AS abs_num , --OR whatever the PK is
COUNT(DISTINCT registers.regeister_id) as reg_num,
students.student_id
FROM students
JOIN seminargroup_student
ON seminargroup_student.student_id = students.student_id
JOIN seminar_group
ON seminar_group.seminar_group_id =
seminargroup_student.seminar_group_id
JOIN modules
ON modules.module_id = seminar_group.seminar_group_module_id
LEFT JOIN register_seminar
ON register_seminar.seminar_id = seminar_group.seminar_group_id
LEFT JOIN registers
ON registers.register_id = register_seminar.register_id
LEFT JOIN absence
ON students.student_id = absence.student_id
GROUP BY
students.student_id) COUNTS
ON a.student_id = coutnts.student_ID
Try to rewrite you query as:
SELECT students.student_id, TRUNCATE((100 - ((100/B.reg_num) * C.abs_num)), 0) AS attendance FROM
students
JOIN (
SELECT COUNT(*) AS reg_num
FROM students A
JOIN seminargroup_student ON seminargroup_student.student_id = A.student_id
JOIN seminar_group ON seminar_group.seminar_group_id = seminargroup_student.seminar_group_id
JOIN modules ON modules.module_id = seminar_group.seminar_group_module_id
JOIN register_seminar ON register_seminar.seminar_id = seminar_group.seminar_group_id
JOIN registers ON registers.register_id = register_seminar.register_id
HAVING modules.module_id =1 AND students.student_id = A.student_id
) B
JOIN (
SELECT COUNT(*) AS abs_num
FROM students A
JOIN seminargroup_student ON seminargroup_student.student_id = A.student_id
JOIN seminar_group ON seminar_group.seminar_group_id = seminargroup_student.seminar_group_id
JOIN modules ON modules.module_id = seminar_group.seminar_group_module_id
JOIN absence ON A.student_id = absence.student_id
HAVING modules.module_id =1 AND students.student_id = A.student_id
) C
UPD: WHERE clause is replaced with HAVING and alias is moved inside sub-queries.
Error may be caused due to WHERE is calculated before SELECT.
Also these questions may help you solve the issue:
1. Unknown Column In Where Clause
2. WHERE vs HAVING

Using GROUP_CONCAT on multiple fields

Here's a picture of my database structure:
With help from users on here I've managed to put together quite a complex SQL statement using GROUP_CONCAT:
SELECT
t1.Name AS Teacher_Name,
t2.Name AS Observer_Name,
o.Datetime AS Datetime,
o.Type AS Type,
o.Year_Group AS Year_Group,
o.Class_Name AS Class_Name,
c.Title AS Course_Name,
GROUP_CONCAT(l.Title) AS Focus,
o.Achievement_Grade AS Achievement_Grade,
o.Behaviour_Grade AS Behaviour_Grade,
o.Teaching_Grade AS Teaching_Grade,
GROUP_CONCAT(cl1.Title) AS Positive,
GROUP_CONCAT(cl2.title) AS Development,
o.Notes AS Notes
FROM observations o
LEFT JOIN teachers t1
ON o.Teacher_ID = t1.Teacher_ID
LEFT JOIN teachers t2
ON o.Observer_ID = t2.Teacher_ID
LEFT JOIN courses c
ON o.Course_ID = c.Course_ID
LEFT JOIN foci f
ON o.ID = f.Observation_ID
LEFT JOIN focus_labels l
on f.focus_id = l.id
LEFT JOIN criteria c1
ON o.ID = c1.Observation_ID
LEFT JOIN criteria_labels cl1
on c1.Criteria_ID = cl1.ID AND c1.Type = 'P'
LEFT JOIN criteria c2
ON o.ID = c2.Observation_ID AND c2.Type = 'D'
LEFT JOIN criteria_labels cl2
on c2.Criteria_ID = cl2.ID
GROUP BY o.id
ORDER BY `Datetime` DESC";
This appears to work OK, apart from the fact that Focus, Positive and Development are each repeated depending on the field that has the highest number of concatenations in.
For example, if Positive has Pace,Progress,Attainment but Focus is only Appraisal, it'll be repeated three times (Appraisal,Appraisal,Appraisal).
I've looked this up and I think it could be because I need to GROUP each of these GROUP_CONCAT JOINs. However, I have no idea how to go about this.
Can anyone help? Thanks in advance,
GROUP_CONCAT has DISTINCT attribute that can be applied to remove duplicates.
SELECT
t1.Name AS Teacher_Name,
t2.Name AS Observer_Name,
o.Datetime AS Datetime,
o.Type AS Type,
o.Year_Group AS Year_Group,
o.Class_Name AS Class_Name,
c.Title AS Course_Name,
GROUP_CONCAT(DISTINCT l.Title) AS Focus,
o.Achievement_Grade AS Achievement_Grade,
o.Behaviour_Grade AS Behaviour_Grade,
o.Teaching_Grade AS Teaching_Grade,
GROUP_CONCAT(cl1.Title) AS Positive,
GROUP_CONCAT(cl2.title) AS Development,
o.Notes AS Notes
FROM observations o
LEFT JOIN teachers t1
ON o.Teacher_ID = t1.Teacher_ID
LEFT JOIN teachers t2
ON o.Observer_ID = t2.Teacher_ID
LEFT JOIN courses c
ON o.Course_ID = c.Course_ID
LEFT JOIN foci f
ON o.ID = f.Observation_ID
LEFT JOIN focus_labels l
on f.focus_id = l.id
LEFT JOIN criteria c1
ON o.ID = c1.Observation_ID
LEFT JOIN criteria_labels cl1
on c1.Criteria_ID = cl1.ID AND c1.Type = 'P'
LEFT JOIN criteria c2
ON o.ID = c2.Observation_ID AND c2.Type = 'D'
LEFT JOIN criteria_labels cl2
on c2.Criteria_ID = cl2.ID
GROUP BY o.id
ORDER BY `Datetime` DESC";

Regarding JOIN query in Mysql

Here is my database structure:
payment option:
mshiptype_id
paymopt_Id
membertopaymentoption:
member_Id
paymopt_Id
mshiptypes
mshiptype_name
mshiptype_id
timetable_id
timetables
timetable_id
timetable_Name
timeslots
timeslot_id
timeslot_name
timeslottotimetables
timeslot_id
timetable_id
I want to get the mshiptype_name and timeslot_name and timetable_name for single member
by using above tables
would any one give suggestions on join query......in mysql
SELECT mshiptype_name, timeslot_name, timetable_name
FROM mshiptypes ST
INNER JOIN paymentoption PO ON ST.mshiptype_id = PO.mshiptype_id
INNER JOIN membertopaymentoption MPO ON PO.paymopt_Id = MPO.paymopt_Id
INNER JOIN timetables TT ON ST.timetable_id = TT.timetable_id
INNER JOIN timeslottotimetables TTT ON TT.timetable_id = TTT.timetable_id
INNER JOIN timeslotss TS ON TTT.timeslot_id = TS.timeslot_id
WHERE MPO.member_id = <members_id>
I think this is what you want:
SELECT mshiptypes.mshiptype_name, timeslots.timeslot_name, timetables.timetable_Name
FROM membertopaymentoption
INNER JOIN paymentoption ON membertopaymentoption.member_Id=paymentoption.paymopt_Id
INNER JOIN mshiptypes ON paymentoption.mshiptype_id=mshiptypes.mshiptype_id
INNER JOIN timetables ON mshiptypes=timetable_id=timetables.timetable_id
INNER JOIN timesslottotimetables ON timetables.timetable_id=timeslottotimetables.timeslot_id
INNER JOIN timeslots ON timeslottotimetables.timeslot_id=timeslots.timeslot_id
WHERE membertopaymentoption.member_Id=$id
Note: replace $id in the WHERE clause with the actual memeber id
The trick is to identify fields common to two different tables, and try to find a path linking all the fields you want.
select
mtpo.member_Id,
mt.mshiptype_name,
ts.timeslot_name,
tt.timetable_name
from
payment_option po
left join membertopaymentoption mtpo on po.paymopt_Id = mtpo.paymopt_Id
left join mshiptypes mt on mt.mshiptype_id = po.mshiptype_id
left join timetables tt on tt.timetable_id= mt.timetable_id
left join timeslottotimetables tstt on tstt.timetable_id = tt.timetable_id
left join timeslots ts on ts.timeslot_id = tstt.timeslot_id
where
member_Id = 1 -- change this
Also, I suggest tidying up the names of your fields and tables before it gets too late. They are quite messy!
select mt. mshiptype_name, ts. timeslot_name, tt. timetable_Name
from membertopaymentoption mtp
inner join payment p on mtp. paymopt_Id = p.paymopt_Id
inner join mshiptypes mt on p.mshiptype_id = m.mshiptype_id
inner join timetables tt on mt.timetable_id = tt. timetable_id
inner join timeslottotimetables tstt on tt timetable_id = tstt. timetable_id
inner join timeslots ts on tstt. timeslot_id = ts. timetable_id
where mtp. member_Id = #memID //will be provided
select
mshiptype_name,
timeslot_name,
timetable_name
from
paymentoption
natural join membertopaymentoption
natural join mshiptypes
natural join timetables
natural join timeslottotimetables
natural join timeslots
where
member_Id = 1