Better approach to solving this Mysql query - mysql

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

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

Getting the best 6 subjects including a compulsory subject in mysql query

I have a student grade system, that calculates a student final mark. A student, for example, can do 10 subjects, but only the best 6 are calculated to come up with the student average.
The current code I have calculates all the marks and divides by the number of subjects. For example, if a student has 8 subjects the system will add all the 8 subjects marks and divide by 8.
Yet it has to select the best 6 inclusive of English which is a passing/compulsory subject.
I have a table that stores the student marks
INSERT INTO `assesmentxmarks`(`id`, `student_id`, `teacher_id`, `assessement_id`, `subject_id`, `class_id`, `mark`, `added`) VALUES (NULL,stu-23,teacher-34,T1,Ssub3,1A,93,NOW())
Above is the table-structure for the table where marks are entered.
SELECT ROUND((SUM(mark)/2)/8) as AVG FROM `assesmentxmarks` WHERE student_id='stu-23'
This will calculate the average but will take all 8 subjects and divide by 8, when it is supposed to only take the best 6 subjects (the top 6 marks)
In this case, the students had written 2 tests and then the query gets the subject average of the two tests by dividing by 2 and then divides all the subject averages by 8 since the student does 8 subjects.
how can I only calculate taking in only the top 6 marks (inclusive of English, even if it is not in the top 6 categories) and calculate the student average
This query:
select * from assesmentxmarks
where student_id = 'stu-23'
order by (subject_id = 'English') desc, mark desc
limit 6
returns the rows that you want.
So find the average of the marks of the those rows:
select round(avg(t.mark), 1) average_mark
from (
select * from assesmentxmarks
where student_id = 'stu-23'
order by (subject_id = 'English') desc, mark desc
limit 6
) t
See the demo.
Result:
| average_mark |
| ------------ |
| 68.3 |

SQL: Fetch rows having a column (group by column) being the MAX value

I would like to know how to retrieve rows matching the maximum value for a column.
SCHEMA
assignments:
id student_id subject_id
1 10 1
2 10 2
3 20 1
4 30 3
5 30 3
6 40 2
students:
id name
10 A
20 B
30 C
subjects:
id name
1 Math
2 Science
3 English
Queries:
Provide the SQL for:
1. Display the names of the students who have taken most number of assignments
2. Display the names of the subjects which have been taken the most number of times
Results:
1.
A
C
2.
Math
English
Thanks !
The previous answer is not quite right - you won't get the instances where there are two with the same count. Try this - the second will be easy to replicate once understand the concept.
SELECT a.student_id, s.name, COUNT(a.subject_id) as taken_subjects
FROM assignments a
INNER JOIN students s ON a.student_id = s.id
GROUP BY a.student_id, s.name
HAVING COUNT(a.subject_id) = (SELECT COUNT(*) FROM assignments GROUP BY student_id LIMIT 1)
Alternate query:
SELECT a.subject_id, s.subject_name, COUNT(a.subject_id) FROM assignment a, subjects s
WHERE a.subject_id = s.subject_id
GROUP BY a.student_id, s.subject_name
HAVING COUNT(a.subject_id) = (SELECT MAX(COUNT(1)) FROM assignment GROUP BY subject_id)

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. :-)

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.