complex query in mysql - 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. :-)

Related

SQL nested query under WHERE

One of the test questions came by with following schemas, to look for the best doctor in terms of:
Best scored;
The most times/attempts;
For each medical procedures (in terms of name)
[doctor] table
id
first_name
last_name
age
1
Phillip
Singleton
50
2
Heidi
Elliott
34
3
Beulah
Townsend
35
4
Gary
Pena
36
5
Doug
Lowe
45
[medical_procedure] table
id
doctor_id
name
score
1
3
colonoscopy
44
2
1
colonoscopy
37
3
4
ulcer surgery
98
4
2
angiography
79
5
3
angiography
84
6
3
embolization
87
and list goes on...
Given solution as follow:
WITH cte AS(
SELECT
name,
first_name,
last_name,
COUNT(*) AS procedure_count,
RANK() OVER(
PARTITION BY name
ORDER BY COUNT(*) DESC) AS place
FROM
medical_procedure p JOIN doctor d
ON p.doctor_id = d.id
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
GROUP BY
name,
first_name,
last_name
)
SELECT
name,
first_name,
last_name
FROM cte
WHERE place = 1;
It'll mean a lot to be clarified on/explain on how the WHERE clause worked out under the subquery:
How it worked out in general
Why must we match the two pp.name and p.name for it to reflect the correct rows...
...
WHERE
score >= (
SELECT AVG(score)
FROM medical_procedure pp
WHERE pp.name = p.name)
...
Thanks a heap!
Above is join with doctor and medical procedure and group by procedure name and you need doctor names with most attempt and best scored.
Subquery will join by procedure avg score and those who have better score than avg will be filtered.
Now there can be multiple doctor better than avg so taken rank by procedure count so most attempted will come first and then you taken first to pick top one

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

Second max based on Category in SQL

I am trying to find the second max based on two different categories. I can use analtycal function or logic to get this. I have been trying to find this through a logic.
My question is I am trying to fetch the records of second most taken exam per country by unique students.
T1
Exam_ID Student_ID
123 553
123 457
345 563
567 765
678 543
678 543
987 123
678 123
T2
Exam_ID Exam_name Country_name
123 SAT USA
345 CAT USA
567 GRE USA
678 TOEFL UK
987 IELTS UK
222 CBAP UK
This is what I tried so far,
select count(distinct T1.Student_ID) count_user,
t2.Country_name,t2.Exam_name
from T1
join T2
on T1.Exam_ID = T2.Exam_ID
group by t2.Exam_name, t2.Country_name
By doing this I am able to get the unique student count based on each exam and country.
How can I get the second max no of exams taken by unique students based on the country?
I'm not sure I fully understand what you mean by your question.
Could you post the expected result along with what you are getting now?
In the mean time, I'm taking a guess that exam_id 678 in the UK (with 3 students) is the top result and 987 in the UK is the "second top result"???
If so, Row_number () might work for you. Bear in mind that row_number is usually an expensive operation in relational databases as it involves a redistribution and a sort. A similar function Rank () may be better for you depending upon how you want to handle ties. The syntax is similar, you could try both.
Try modifying your query as follows:
select count(distinct T1.student_id) count_user, Country_name, Exam_name,
row_number () over (partition by country_name order by count_user desc) as row_num
...
If that gives you the numbering you want, you can then restrict the output using the qualify clause i.e.
qualify row_num = 2
You may need to wrap the whole thing in a derived table as follows:
select count_user, country_name, exam_name,
row_number () over (partition by country_name order by count_user desc) as row_num
from (
select count(distinct T1.Student_ID) count_user,
t2.Country_name,t2.Exam_name,
from T1 join T2
on T1.Exam_ID = T2.Exam_ID
group by t2.Exam_name, t2.Country_name
) detail_recs
qualify row_num = 2

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.

how to use union resolve this full outer join problem under 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.