I am trying to find the name of students where a symmetric pair exists. There are 3 tables:
**student**
student_id (Primary Key) | smallint
student_name | varchar(30)
**mathematics_marks**
student_id (Primary Key) | smallint
score | float (5,2)
**science_marks**
student_id (Primary Key) | smallint
score | float (5,2)
with Functions as (
select s.student_name as name, mm.score as math_score, sm.score as science_score
from student s
join mathematics_marks mm
on mm.student_id = s.student_id
join science_marks sm
on sm.student_id = s.student_id)
select t1.name
from Functions t1
join Functions t2
on t1.math_score = t2.science_score
and t1.science_score = t2.math_score
where t1.math_score < t1.science_score
Edit from your comment: A student is called as being a part of a symmetric pair if the marks obtained by that student in science is equal to the marks obtained by some other student in mathematics and the marks obtained in mathematics are the same as marks obtained by the other student in science.
Given the structure of the data, I would assume that students could have multiple marks in each subject. Otherwise, why store the values in separate tables?
To solve this problem, I would preaggregate the marks:
with mm as (
select student_id, group_concat(score order by score desc) as math_scores
from mathematics_marks
group by student_id
),
sm as (
select student_id, group_concat(score order by score desc) as science_scores
from science_marks
group by student_id
),
sms as (
select *
from mm join
sm
using (student_id)
)
select sms.student_id, sms2.student_id
from sms join
sms sms2
on sms.math_scores = sms2.science_scores and
sms.science_scores = sms2.math_scores and
sms.student_id < sms2.student_id;
This returns the matching ids. You need an additional join if you want to bring in the names.
Note: You have stored the values as floats. This is quite dangerous. You should be storing the values as decimal/numeric. Two values that look the same might actually be different.
This is how I would write the code for this requirement:
with cte as (
select m.student_id, m.score as math_score, s.score as science_score
from mathematics_marks m inner join science_marks s
on s.student_id = m.student_id
)
select s1.student_name, s2.student_name
from cte c1 inner join cte cte2
on c2.student_id > c1.student_id and c2.math_score = c1.science_score and c1.math_score = c2.science_score
inner join student s1 on s1.student_id = c1.student_id
inner join student s2 on s2.student_id = c2.student_id
Try this
select s.student_name as name, mm.score as math_score, sm.score as science_score
from student s
join mathematics_marks
mm
on mm.student_id
<s.student_id
join science_marks sm
on sm.student_id <s.student_id and
mm.math_score
=sm.science_score
Related
Given the tables student, mathematics_marks, science_marks
student
student_id (Primary Key) | smallint
student_name | varchar(30)
mathematics_marks
student_id (Primary Key) | smallint
score | float (5,2)
science_marks
student_id (Primary Key) | smallint
score | float (5,2)
A student is called as being a part of a symmetric pair if the marks obtained by that student in science is equal to the marks obtained by some other student in mathematics and the marks obtained in mathematics are the same as marks obtained by the other student in science.
Write a SQL query to print the name of the students who are a part of the symmetric pair. Order the output in order of student name.
with cte as (
select m.student_id, m.score as math_score, s.score as science_score
from mathematics_marks m inner join science_marks s
on s.student_id = m.student_id
)
select s1.student_name
from cte c1 inner join cte c2
on c2.student_id > c1.student_id and c2.math_score = c1.science_score and c1.math_score = c2.science_score
inner join student s1 on s1.student_id = c1.student_id and s1.student_id = c2.student_id
need to get the pair in two different rows?
EDITED:
answer to this
with cte as (
select m.student_id, m.score as math_score, s.score as science_score
from mathematics_marks m inner join science_marks s
on s.student_id = m.student_id
),
T as (select c1.student_id, c1.math_score, c1.science_score, s.student_name as name
from cte c1 inner join student s
on c1.student_id = s.student_id )
select t1.name from T t1 inner join T t2
on t1.math_score = t2.science_score and
t1.science_score = t2.math_score
order by t1.name
A properly designed schema would have a single table for student, subject, and mark. The first step then is to emulate that table, which could be as follows:
SELECT student_id
, 'mathematics' subject
, score
FROM mathematics_marks
UNION
SELECT student_id
, 'science'
, score
FROM science_marks
For further help, see: Why should I provide an MCRE for what seems to me to be a very simple SQL query?
It's always easy to answer if the post is accompanied by sample data and expected output . Are you looking for somthing like this
Select student.student_id
,maths.score maths_socer
,sci.score science_score
from
(
Select 1 student_id union all
Select 2 union all
Select 3
) student
Join
(
Select 1 student_id, 20 score union all
Select 2, 30 union all
Select 3, 40
) maths
on maths.student_id=student.student_id
Join
(
Select 1 student_id , 30 score union all
Select 2,20 union all
Select 3,40
) sci on sci.student_id !=student.student_id
Where sci.score = maths.score
Given the tables student, mathematics_marks, science_marks
student
student_id (Primary Key) | smallint
student_name | varchar(30)
mathematics_marks
student_id (Primary Key) | smallint
score | float (5,2)
science_marks
student_id (Primary Key) | smallint
score | float (5,2)
A student is called as being a part of a symmetric pair if the marks obtained by that student in science is equal to the marks obtained by some other student in mathematics and the marks obtained in mathematics are the same as marks obtained by the other student in science.
I am trying to solve the above problem with the following query:
SELECT s.student_name
FROM student s
LEFT JOIN (mathematics_marks m CROSS JOIN science_marks sc)
ON (s.student_id = m.student_id AND m.student_id = sc.student_id)
WHERE EXISTS(SELECT * FROM mathematics_marks m
WHERE sc.score=m.score
AND m.score=sc.score)
ORDER BY student_name;
I am not getting correct output. Can anyone help me out where i am going wrong?
In a very simple way
select s.student_name 'student_name'
from student s
inner join mathematics_marks m
on m.student_id = s.student_id
inner join science_marks sc
on sc.student_id = s.student_id
where m.score in ( select score from science_marks ) and
sc.score in ( select score from mathematics_marks )
ORDER BY student_name;
I would do something like this:
SELECT s.student_name
FROM student s
LEFT JOIN mathematics_marks m ON m.student_id = s.student_id
LEFT JOIN science_marks sc ON sc.student_id = s.student_id
WHERE m.score IN (SELECT score FROM science_marks) OR sc.score IN (SELECT score FROM mathematics_marks)
Cross Join all students and then compare the marks for each student with other students.
select student1.student_name, student2.student_name
from (
select s1.student_id, s1.student_name, m1.score "m_score", sc1.score "sc_score"
from student s1
join mathematics_marks m1 on s1.student_id = m1.student_id
join science_marks sc1 on s1.student_id = sc1.student_id) student1,
(
select s2.student_id, s2.student_name, m2.score "m_score", sc2.score "sc_score"
from student s2
join mathematics_marks m2 on s2.student_id = m2.student_id
join science_marks sc2 on s2.student_id = sc2.student_id) student2
where student1.student_id<>student2.student_id
and student1.m_score = student2.sc_score
and student1.sc_score = student2.m_score;
For this requirement you need multiple joins of the 2 marks tables and finally to get the names of the students you need to join the table student twice:
select st1.student_name, st2.student_name
from mathematics_marks m1
inner join science_marks s1 on s1.student_id > m1.student_id and s1.score = m1.score
inner join mathematics_marks m2 on m2.student_id = s1.student_id
inner join science_marks s2 on s2.student_id < m2.student_id and s2.score = m2.score
inner join student st1 on st1.student_id = m1.student_id
inner join student st2 on st2.student_id = m2.student_id;
See the demo.
For simplicity and get all score under single table let's create a view.
create view student_marks as select mm.student_id,mm.score as m_score, sm.score as s_score
from mathematics_marks mm
inner join science_marks sm on mm.student_id=sm.student_id;
now we have all marks under single table.
so we need to do self join of above view and find students who satisfy the symmetric condition
select distinct s.*
from student_marks sm1
inner join student_marks sm2 on sm1.m_score=sm2.s_score and sm2.m_score=sm1.s_score
left join student s on s.student_id = sm1.student_id or s.student_id=sm2.student_id;
If you don't want to create view, you can simply replace student_marks with the view query.
I want to do a relational division query. First, here is the structure of each table:
Student
id
name
Course
id
name
Student_passed_course (junction table that stores who passed which course)
id_student
id_course
Basically, what I want is to get the names of the students that have passed all the courses that exist in table Course using JOIN (or LEFT JOIN, etc). I already implemented a solution using NOT EXISTS.
Also this is the equation I made.
A solution based on JOIN could be as follow:
SELECT s.name AS student_name, c.name as course_name FROM (
SELECT s_id, c_id, SUM(c_exists) AS c_exists, SUM(c_taken) as c_taken, SUM(c_exists)-SUM(c_taken) as not_taken FROM (
SELECT s.id as s_id, c.id as c_id, 1 as c_taken, 0 as c_exists FROM student s JOIN student_passed_course spc ON spc.id_student = s.id JOIN course c ON c.id = spc.id_course
UNION ALL
SELECT s.id as s_id, c.id as c_id, 0 as c_taken, 1 as c_exists FROM student s JOIN course c
) X group BY s_id, c_id) Y
JOIN student s ON s.id = s_id JOIN course c ON c_id = c.id
WHERE not_taken = 1;
I don't know if this would be more or less efficient than your solution.
Suppose I have three tables
Student Student_Interest Interest
======= ================ ========
Id Student_Id Id
Name Interest_Id Name
Where Student_Interest.Student_Id refers to Student.Id
and Student_Interest.Interest_Id refers to Interest.Id
Let's say we have three kinds of interest viz. "Java", "C", "C++" and "C#" and there are some entries in the student table and their respective interest mapping entries in the Student_Interest table. (A typical many-to-many relationship)
How can we get the list of students that have both "Java" and "C" as their interests?
I already see a couple of correct answers, but anyway, here is my one:
select s.* from student s
join (
select si.student_id from student_interest si join interest i on i.id = si.interest_id
where i.name in ('Java','C') group by si.student_id having count(*) = 2
) iv on iv.student_id = s.id
Simply get the Java and C records from student_interest, group by student and see if you get the complete number of interests for a student. With such students found you can display data from the student table.
select *
from student
where id in
(
select student_id
from student_interest
where interest_id in (select id from interest where name in ('Java', 'C'))
group by student_id
having count(distinct interest_id) = 2
);
EDIT: You've asked me to show a query with EXISTS. The straight-forward way would be:
select *
from student
where exists
(
select *
from student_interest
where student_id = student.id
and interest_id = (select id from interest where name = 'Java')
)
and exists
(
select *
from student_interest
where student_id = student.id
and interest_id = (select id from interest where name = 'C')
);
For every interest an additional EXISTS clause. If, however, you want to convert the IN query above to an EXISTS query, so to have only one EXISTS clause, you get:
select *
from student
where exists
(
select student_id
from student_interest
where student_id = student.id
and interest_id in (select id from interest where name in ('Java', 'C'))
group by student_id
having count(distinct interest_id) = 2
);
I find the IN clause more readable, but that's a matter of taste, I guess.
How can we get the list of students that have both "Java" and "C" as
their interests?
We can write t(t.c,...) to say that row (t.c,...) is in table t. Let's alias Student to s, Student_Interest to sij and sic and Interest to ij and ic. We want rows (s.Id,s.Name) where
s(s.Id,s.Name)
AND sij(sij.Student_Id,sij.Interest_Id) AND s.Id = sij.Student_Id
AND sic(sic.Student_Id,sic.Interest_Id) AND s.Id = sic.Student_Id
AND ij(ij.Id,ij.Name) AND ij.Id=sij.Interest_Id AND ij.Name = 'Java'
AND ic(ic.Id,ic.Name) AND ic.Id=sic.Interest_Id AND ic.Name = 'C'
So:
select s.Id,s.Name
from Student s
join Student_Interest sij on s.Id = sij.Student_Id
join Student_Interest sic on s.Id = sic.Student_Id
join Interest ij on ij.Id=sij.Interest_Id AND ij.Name = 'Java'
join Interest ic on ic.Id=sic.Interest_Id AND ic.Name = 'C'
You will need to join all the three tables to get the information as below:
SELECT s.*
FROM Student s, Student_Interest si, Interest i, Interest sin
WHERE s.Id = si.Student_Id
AND i.Id = si.Interest_Id
AND sin.Id = si.Interest_Id
AND i.Name = 'Java'
AND sin.Name = 'C'
Do a JOIN with all the tables. You need to GROUP BY the column Name to get both interest like below. See a demo fiddle http://sqlfiddle.com/#!2/e80391/13
select s.Name
from student s
join Student_Interest si on s.id = si.Student_Id
join Interest i on si.Interest_Id = i.id
join Interest ii on si.Interest_Id = ii.id
group by s.Name
having count(*) > 1
I am having trouble trying to create a query to:
Select all the students who have not completed all peer review's for a particular week.
background: Each week, every student must peer review their peers in the same group.
Each group can be a different size, which is the problem I am having.
this is my current test data:
Table 1: peer review table
Table 2: student table.
This is my inital query, groups all the students based on the amount of peer review's they've made. I now need to to check if the count(*) is less than the size of the group for each student :
SELECT *
FROM peerreview
RIGHT JOIN student
ON student. studentID = peerreview.reviewer
WHERE week = 11
GROUP BY studentID
HAVING Count(*) < ????
Following query will return the student which has reviewed all the students in same group.
SELECT a.reviewer,
a.groupid
FROM (SELECT student2.studentID AS reviewer,
student1.groupid,
Count(*) AS cnt
FROM student student1
INNER JOIN peerreview
ON student1.studentID = peerreview.reviewee
INNER JOIN STUDENT STUDENT2
ON student2.studentID = peerreview.reviewer
WHERE student2.groupid = student2.groupid
AND peerreview.week = 11
GROUP BY student1.groupid,
student2.studentID) a
INNER JOIN (SELECT groupid,
Count(*) - 1 AS cnt
FROM student
GROUP BY groupid) b
ON a.groupid = b.groupid
AND a.cnt = b.cnt
See SqlFiddle
Select S.StudentId As Reviewer
, S1.StudentId As StudentYetToBeReviewed
, Weeks.WeekNum
From Student As S
Join Student As S1
On S1.GroupId = S.GroupId
And S1.StudentId <> S.StudentId
Cross Join (
Select 7 As WeekNum
Union All Select 11
) As Weeks
Where Not Exists (
Select 1
From PeerReview As P1
Where P1.reviewee = S1.StudentId
And P1.Week = Weeks.WeekNum
)
Order By WeekNum, reviewer
This provides you a list, by week, of the reviewer and the person they need to review. In the real solution, you would want to replace the Cross Join of weeks with a distinct list of weeks in which reviews should happen.
SQL Fiddle version
select distinct s1.*
from student s1 inner join student s2 on s1.groupId = s2.groupeId
left join peerreview pr on pr.revieweer = s1.studentId
and pr.reviewee = s2.studentId
where pr.Week = ? and pr.revieweer is null and s1.studentId <> s2.studentId