how to use union resolve this full outer join problem under mySQL - mysql

Here is the table
stuid stuname subject grade
1 alex algo 99
1 alex dastr 100
2 bob algo 90
2 bob dastr 95
3 casy algo 100
4 Daisy dastr 100
case1: assuming there are only two subjects in the table
Following is the expected output
stuname algo dastr
alex 99 100
bob 90 95
casy 100 0
Daisy 0 100
I think following is a workable query
select g1.stuname,
COALESCE(g1.grade,0) as algo
COALESCE(g2.grade,0) as dastr
from grades g1
full outer join grades g2 on g1.stuid = g2.stuid
where g1.subject = algo and g2.subject = dastr;
But, mysql doesnt support full outer join. Is there any other way to resolve the problem?
Also, case 2
assuming there are unknown number of subjects in the table
and the expected output would be
stuname subj1 subj2 subj3 ... subjn
I know I might be using procedure resolve it, is there any other way that I can use to compose columns in mySQL?

Your queries would work better if you re-structured your tables. You are attempting to store too much information in one table. Here is a proposed structure:
Students
student_id student_name
1 Alex
2 Bob
3 Casy
4 Daisy
Subjects
subject_id subject_name
1 Algo
2 Dastr
Grades
student_id subject_id grade
1 1 99
1 2 100
2 1 90
2 2 95
3 1 100
4 2 100
In grades, student_id and subject_id would be a composite key, meaning a unique combination of the two becomes the unique identifier (student 1, subject 1 is unique from student 1, subject 2)
To return the data based on your comment, try:
SELECT a.student_name, b.subject_name, c.grade
FROM students a, subjects b, grades c
WHERE a.student_id = c.student_id
AND b.subject_id = c.subject_id
ORDER BY a.student_id

Have you tried something along the line of:
SELECT a.stuid as sidA, a.grade as grA, a.grade as grB
FROM grades a JOIN grades b ON (a.stuname = b.stuname)
But as D.N. suggested, it may be worth restructuring your tables

From your existing data...
select
stuid,
max( stuName ) stuName,
max( if( subject = "algo", grade, 000 )) as Algo,
max( if( subject = "dastr", grade, 000 )) as Dastr
from
Grades
group by
stuid
order by
stuName
However, if you have multiple people with the same "StuName", by grouping by their unique ID, it will keep them differentiated, so for clarification, I've included the ID column in the final query.
However, the data restructuring as suggested by #D.N. would be a cleaner approach.

Related

Better approach to solving this Mysql query

I have two tables similar to the below examples. I wrote a query to combine the two tables and get the total score of the students. The total score consists of (caone+catwo+examscore). I am searching to see if there are other better approaches to solving this in terms of performance and also syntax wise. Thanks
ca table
name id course ca_cat score
one 1 maths 1 10
one 1 maths 2 6
two 2 maths 1 9
two 2 maths 2 7
exam table
name id course score
one 1 maths 50
two 2 maths 49
My query is shown below
WITH
firstca AS (
SELECT
id,
name,
score,
subject,
FROM
ca
WHERE
cacount =1 ),
secondca AS (
SELECT
id,
name,
score,
subject,
FROM
ca
WHERE
cacount=2),
exam AS (
SELECT
id,
name,
score,
subject,
FROM
exam),
totalscore AS (
SELECT
fca.studentid,
fca.name,
fca.subject,
fca.score AS firstcascore,
sca.score AS secondcascore,
ex.score AS examscore,
(fca.score +sca.score) AS totalca,
(fca.score+sca.score+ex.score) AS totalscores,
FROM
firstca AS fca
JOIN
secondca AS sca
ON
fca.studentid=sca.studentid
AND fca.subject=sca.subject
JOIN
exam AS ex
ON
fca.studentid=ex.studentid
AND fca.subject=ex.subject
The final result table can be similar to this
name id course caone catwo exam totalscore
one 1 maths 10 6 50 66
two 2 maths 9 7 49 65
Is there a better way to write this query, maybe without the with statement or using subqueries and unions?
I wish to learn from every answer here.
Below is for BigQuery Standard SQL
#standardSQL
SELECT name, id, course, caone, catwo, exam,
caone + catwo + exam AS totalscore
FROM (
SELECT name, id, course,
MAX(IF(ca_cat = 1, t2.score, NULL)) AS caone,
MAX(IF(ca_cat = 2, t2.score, NULL)) AS catwo,
ANY_VALUE(t1.score) AS exam
FROM `project.dataset.exam` t1
JOIN `project.dataset.ca` t2
USING (name, id, course)
GROUP BY name, id, course
)
If to apply to sample data from your question - output is
Row name id course caone catwo exam totalscore
1 one 1 maths 10 6 50 66
2 two 2 maths 9 7 49 65

sql select master records based on ANDing multiple detail records

I have master and detail tables as described below (with representative data). I want to select (mysql-compliant) Student.id for students that have an "A" in both Biology and Chemistry.
Students grades
id name id student_id class grade
1 ken 1 1 Biology A
2 beth 2 1 Chemistry A
3 joe 3 1 Math B
4 2 Biology A
5 2 Chemistry A
6 2 Math A
7 3 Biology B
8 3 Chemistry A
9 3 Math A
Currently, I'm just pulling in all the data into my program (java) but figure there's got to be a way in SQL to get the right records.
The results I'm looking for from the data above would be 1 & 2 (ken and beth). I've tried a few variations using joins and inner selects but can't quite get it to work. My main problem seems to be I'm ANDing my detail records eg., ...where grades.class='Biology' and grades.grade='A'
I took a look at SQL select from header table where detail table rows have multiple values but that didn't quite get me where I need to be.
Assistance greatly appreciated.
Try this
select s.id from students as s
inner join grades as g on s.id=g.student_id
group by s.id
having max(case when g.class='Biology' and g.grade='A' then 1 else 0 end)=1
and
having max(case when g.class='Chemistry' and g.grade='A' then 1 else 0 end)=1
Here is one way:
select g.student_id
from grades g
where g.class in ('Biology', 'Chemistry') and g.grade = 'A'
group by g.student_id
having count(distinct class) = 2;
Notes:
A join is not necessary because the grades table has the student id.
The where clause only gets records where a student has an 'A' in either class.
The having guarantees that a student has an 'A' in both classes.
I should note that there is an alternative method that uses join but not group by:
select gb.student_id
from grades gb join
grades gc
on gb.student_id = gc.student_id and
gb.class = 'Biology' and gc.class = 'Chemistry' and
gb.grade = 'A' and gc.grade = 'A';
This works -- and the performance might even be better. I like the group by and having approach because it is more flexible.

Multiple join with self join in MySQL and split rows in columns by a row value

I have three tables "Users" , "Subjects" and "Marks" like
Users Table
id name
1 A
2 B
3 C
4 D
5 E
6 A
7 B
Subjects Table
id name
1 Chemistry
2 Physics
3 English
4 Maths
5 History
Marks Table
u_id is the foreign key of Users (id) and s_id is foreign key of Subjects(id)
id u_id s_id marks
1 1 1 60
2 1 2 70
3 1 3 80
4 2 2 80
5 2 3 44
6 3 1 50
7 5 4 50
8 4 5 50
9 5 4 100
10 2 5 100
and I wish for the result to be like
id Name Chemistry Physics English
1 A 60 70 80
2 B NULL 80 44
3 3 50 NULL NULL
Using Join
So far I have only been able to get
name name marks
A English 80
A Physics 70
A Chemistry 60
B English 44
B Physics 80
C Chemistry 50
Using the following query
SELECT u.name, s.name , m.marks
FROM Users as u
RIGHT JOIN Marks as m ON m.u_id = u.id
LEFT JOIN Subjects as s ON m.s_id = s.id
WHERE s.name = "English"
OR s.name = "Physics"
OR s.name = "Chemistry"
ORDER BY u.name; "
Well, after reading the answers, I wanted to post my own one:
SELECT
u.id
, u.name
, MAX(IF(s.id = 1, COALESCE(m.mark), 0)) as 'Chem'
, MAX(IF(s.id = 2, COALESCE(m.mark), 0)) as 'Phys'
, MAX(IF(s.id = 3, COALESCE(m.mark), 0)) as 'Eng'
FROM marks m
INNER JOIN subjects s
ON s.id = m.subjects_id
INNER JOIN users u
ON u.id = m.users_id
GROUP BY u.id
You can check that makes all you want in SqlFiddle: http://sqlfiddle.com/#!9/f567b/1
The important part is the grouping of all the elements according to the user id, and the way of writing the results from rows in a table to columns in another table. As written in #TheShalit answer, the way of achieving that is just assigning the value as a column. Problem is that when grouping by user, you'll have a lot of values there from where you have to select the important one (the one that is not 0 neither NULL, XD). COALESCE function makes sure that you always return a integer, just in case a NULL is given.
It's also important to notice that you'll have to build the SQL with the names of the subjects and the ids from database, as SQL can't retrieve the name of the elements to write them directly as names of the columns. That's why I wrote 'Chem', 'Phys' and 'Eng' instead of the right names. In fact, would be easier if you just wrote the id of the subject instead of a name, just to retrieve the elements later when you'll fetch the rows.
Take into account that is VERY IMPORTANT that you'll table will have the right indexes there. Make sure you have an UNIQUE id on the table marks with users and subjects to avoid having more than one value there stored
Use select like this(with joins and group by student):
MAX(If(subjects.name="Chemistry",marks.marks,'')) Chemistry,
MAX(If(subjects.name="Physics",marks.marks,'')) Physics,
.....
You will need to do something like:
SELECT u.NAME AS NAME,
m_e.marks AS english,
m_p.marks AS physics,
m_c.marks AS chemistry
FROM users AS u
JOIN marks AS m_e ON m_e.u_id = u.id
JOIN marks AS m_p ON m_p.u_id = u.id
JOIN marks AS m_c ON m_c.u_id = u.id
WHERE m_e.s_id = 3 AND m_c.s_id = 1 AND m_p.s_id = 2
You are getting 3 different values from a single table but different rows so you need to join the marks table with itself to be able to get the values from 3 different records into 1 result row
I used the values that you defined as primary id's for your 3 subjects in your question in the where clause to make sure you are getting the correct result for each subject

Give the name of all the teacher who does not teach math

I have two tables. One is the Course table and the second is the Teacher table. I want to get all Teacher who does not teach 'Math'. How can I do this?
Course Table
course_id course teacher_id marks
1 Physics 1 60
2 Math 1 60
3 Chemestry 1 60
4 English 2 60
5 Hindi 2 60
6 Physics 2 60
7 Chemestry 3 60
8 English 4 60
9 Math 5 60
10 Math 6 60
Teacher Table
teacher_id name salary gender
1 Teacher1 20 1
2 Teacher2 30 1
3 Teacher3 40 2
4 Teacher4 50 2
5 Teacher5 60 1
6 Teacher6 70 2
I want to get all teacher who does not teachs math.
You need to join both the tables on teacher_id and then filter out the rows based on the course.
SQL> SELECT DISTINCT t.name
2 FROM course c,
3 teacher t
4 WHERE c.teacher_id = t.teacher_id
5 AND c.course <> 'Math';
NAME
--------
Teacher2
Teacher1
Teacher4
Teacher3
SQL>
EDIT Since you have teachers teaching multiple courses, you need to filter out further:
SQL> WITH DATA AS
2 (SELECT c.*,
3 t.name
4 FROM course c,
5 teacher t
6 WHERE c.teacher_id = t.teacher_id
7 AND c.course <> 'Math'
8 )
9 SELECT DISTINCT name
10 FROM data
11 WHERE teacher_id NOT IN
12 (SELECT teacher_id FROM course WHERE course = 'Math'
13 )
14 /
NAME
--------
Teacher2
Teacher4
Teacher3
SQL>
NOTE Please keep in mind that the other solution using NOT EXISTS clause is better in terms of performance, since the table scans are less and even index scans. With proper indexes, the not exists query would be an optimal method.
select *
from teacher t
where not exists
(select 1 from course c where c.teacher_id = t.teacher_id and c.course = 'Math')
#LalitKumarB
Ben is absolutely right
inner join
select t.teacher_id, t.name
from teacher t, Course c
where c.course='math' and t.teacher_id=c.teacher_id;
EDIT
you can do it using join and subquery.
select * from course join teacher
on course.teacher_id=teacher.teacher_id
where teacher.teacher_id not in
(select distinct teacher_id from course where course = 'Math')
Select * from Teacher
join Course
on Teacher.teacher.id = Course.teacher.id
where Course.course != 'Math'
select
t.name
from teacher t
left join course c
on c.teacher_id = t.teacher_id
where c.course_id <> 2

complex query in mysql

i have three tables in mysql like this,
triz_sti
stu_id name
-----------------
1 x1
2 x2
triz_sub
sub_id sub_name
------------------
1 english
2 maths
3 science
triz
stu_id sub_id marks
-------------------------
1 1 23
1 2 56
1 3 83
2 1 78
2 2 23
2 3 50
i want the result like
display all subject with higest mark in perticular subject with student name,
max_marks sub_name student_name
--------------------------------------
78 english x2
56 maths x1
83 science x2
so please help for this output that i want, i have tried but i m not get it desire output.
How about something like this?
SELECT
t.stu_id, t.sub_id, t.marks
FROM
triz t
JOIN (SELECT sub_id, MAX(marks) max_mark FROM triz GROUP BY sub_id) a ON (a.sub_id = t.sub_id AND a.max_mark = t.marks)
Of course you'll need to join it with lookup tables for names.
Have to say, it's early here so I might have missed something.
BR
The general, simplified syntax in this case is
SELECT stuff FROM joined tables ORDER BY whatever
The easiest is the ORDER BY: you want to sort descending by marks, so you ORDER BY marks DESC.
Where do the data come from? From triz, joined to the others. So
triz JOIN triz_sti USING (stu_id) JOIN triz_sub USING (sub_id)
And you want to display the marks.
So you get
SELECT marks, sub_name, name AS student_name
FROM triz JOIN triz_sti USING (stu_id) JOIN triz_sub USING (sub_id)
ORDER BY marks DESC
.
The rest I leave to you. :-)