Add column in query using LEFT JOIN - mysql

I have three tables.
tblcandidates
candidateid | candidatename
1 | Abc
2 | Def
tbljudges
judgeid | judgename
1 | Stack
2 | Overflow
tblscores
scoreid | candidateid | judgeid | swimsuit
1 | 1 | 1 | 100
2 | 1 | 2 | 99
3 | 2 | 1 | 100
4 | 2 | 2 | 93
I am using this query to get the average of each candidate.
SELECT DISTINCT
(c.candidateid) AS c,
candidatename AS NAME,
j1.swimsuit AS j1,
j2.swimsuit AS j2,
(
j1.swimsuit + j2.swimsuit
) / 2 AS average
FROM
tblscores,
tblcandidates c
LEFT JOIN tblscores j1 ON c.candidateid = j1.candidateid
AND j1.judgeid = 1
LEFT JOIN tblscores j2 ON c.candidateid = j2.candidateid
AND j2.judgeid = 2
WHERE tblscores.candidateid = c.candidateid;
Output
c | name | j1 | j2 | average
1 | Abc | 100 | 99 | 99.5
2 | Def | 100 | 93 | 96.5
My problem is what if the judges become 3. I want to make my query dynamic depending on the number of judges. My query is limited to 2 judges only. I also want to display the judges scores like in my output for the proof that they have a score.

You are re-inventing the wheel here by implementing the average calculation yourself. Instead, you could use MySQL's builtin aggregate avg function. If you really want all the scores too, you could use group_concat to display them:
SELECT c.candidateid AS id,
candidatename AS name,
GROUP_CONCAT(swimsuit) AS all_scores,
AVG(swimsuit) AS average_score
FROM tblcandidates c
JOIN tblscores s ON c.candidateid = s.candidateid
GROUP BY c.candidateid, candidatename

Related

How to add default value on sum to have equal number of lines sum

I'm in front of a problem that regarding MySQL.
I have three tables in my Database :
Table gp
____________
id | name |
____________
1 | Le Mans|
2 | Toulon |
3 | Rennes |
Table player
____________
id | name |
____________
1 | Thibaut|
2 | Fred |
3 | Samir |
Table Records
_____________________________
id | gp_id | player_id | time
_____________________________
1 | 1 | 1 | 22
2 | 2 | 1 | 33
3 | 3 | 1 | 44
4 | 3 | 2 | 40
5 | 2 | 2 | 35
6 | 1 | 2 | 20
7 | 1 | 3 | 25
8 | 3 | 3 | 38
I want to get a sum of time for players that have at least one record on some specifics GP and to set a default time for the gp where they don't have a time
I have no idea how I can get that. Actually my SQL query get the values but I don't know how to set a default time for GP not finished by some players.
SELECT p.name, sum(time) as total_time
from records r
join gp g on r.gp_id = g.id
join player p on r.player_id = p.id
where gp.id in ( 1, 2, 3)
having count(distinct g.id) > 0
group by r.player_id
For example for this query I get these values :
name | total_time
_________________
Thibaut | 99
Fred | 95
Samir | 63
But I want a default time to 99 if there is no time for a player in one GP, so in my case Samir should have 63+99 => 162.
But I have no idea how to do that, and I don't know if it's possible
Thanks in advance guys !
Use LEFT JOIN to get a null value if there's no match, and IFNULL() to supply a default value in place of NULL.
Use a CROSS JOIN with gp to specify all the games that should be considered.
SELECT p.name, sum(IFNULL(time, 99)) as total_time
from player p
cross join gp
left join records r on r.player_id = p.id AND r.gp_id = gp.id
WHERE gp.id IN (1, 2, 3)
group by p.id
having count(distinct r.gp_id) > 0
DEMO

How can I not take in consideration values join without record on the db

I'm in front of a "minor" problem taht looks easy but I didn't suceed to resolve it.
I have three tables in my Database :
Table gp
____________
id | name |
____________
1 | Le Mans|
2 | Toulon |
3 | Rennes |
Table player
____________
id | name |
____________
1 | Thibaut|
2 | Fred |
3 | Samir |
Table Records
_____________________________
id | gp_id | player_id | time
_____________________________
1 | 1 | 1 | 17860
2 | 2 | 1 | 11311
3 | 3 | 1 | 33133
4 | 3 | 2 | 11113
5 | 2 | 2 | 44444
6 | 1 | 2 | 13131
7 | 1 | 3 | 11111
8 | 3 | 3 | 21112
I want to get a sum of time for players that have a record on every gp ( so in my case, just players Thibaut and Fred have a record on the 3 gp ( Samir has just a record on two gp ) ).
I have no idea how I can get that, of course this SQL query is retrieving a sum but from this query I want to escape the guys that don't have a record on every GPs, but I'm blocked at that point ...
SELECT p.name, sum(time)
from records r
join gp g on r.gp_id = g.id
join player p on r.player_id = p.id
group by r.player_id
Thanks in advance guys !
You could use having count to exclude the records that don't have a record on every GPs.
Try:
select p.name,
sum(`time`) as tot_sum
from records r
inner join player p on r.player_id=p.id
inner join gp g on g.id=r.gp_id
group by p.name
having count(distinct gp_id) = (select count(distinct id) from gp)
https://dbfiddle.uk/t8QwSFDY
having count(distinct gp_id) = (select count(distinct id) from gp) will match only the records in the record table that have a record on every gp.

SQl - find average amount per person

I have two tables
class
| id | area | students |
| 1 | area1 | 2 |
| 2 | area1 | 28 |
| 3 | area1 | 22 |
| 4 | area2 | 4 |
deliveries
| id | kg | classid |
| 1 | 120 | 1 |
| 2 | 80 | 1 |
| 3 | 20 | 1 |
| 4 | 200 | 2 |
| 5 | 150 | 3 |
| 6 | 14 | 2 |
I need to sum up the average of kg delivered per student in a each area.
For area1 that should amount to (120+80+20+200+150+14)/(2+28+22) = 11.23
But I can't figure out how to write that query. I guess I have to use some kind of subquery to first sum out students in area1 (52), before I sum kg delivered and divide on students?
This is a little tricky, because the students should be counted separately from the classes:
select c.area, sum(d.kg) / max(area_students) as avg_kg_per_student
from class c join
deliveries d
on d.classid = c.id join
(select c2.area, sum(students) as area_students
from class c2
group by c2.area
) c2
on c2.area = c.area
group by c.area;
I think you cannot use average because you need to determine the denominator yourself:
SELECT sum(kg)/ studSum AS avg
FROM _class LEFT JOIN _deliveries ON _class.id=_deliveries.classid
left join (select area, sum(students) as studSum from _class group by area) subT
ON subT.area=_class.area
GROUP BY _class.area;
Here is a very readable approach: Get students per area and kg per area, then join the two.
select stu.area, stu.students, del.kg, del.kg / stu.students
from
(
select area, sum(students) as students
from class
group by area
) stu
join
(
select c.area, sum(d.kg) as kg
from class c
join deliveries d on d.classid = c.classid
group by c.area
) del on del.area = stu.area;

Joining tables but needs 0 for empty rows

I don't know how to explain the scenario using words. So am writing the examples:
I have a table named tblType:
type_id | type_name
---------------------
1 | abb
2 | cda
3 | edg
4 | hij
5 | klm
And I have another table named tblRequest:
req_id | type_id | user_id | duration
-------------------------------------------
1 | 4 | 1002 | 20
2 | 1 | 1002 | 60
3 | 5 | 1008 | 60
....
So what am trying to do is, fetch the SUM() of duration for each type, for a particular user.
This is what I tried:
SELECT
SUM(r.`duration`) AS `duration`,
t.`type_id`,
t.`type_name`
FROM `tblRequest` AS r
LEFT JOIN `tblType` AS t ON r.`type_id` = t.`type_id`
WHERE r.`user_id` = '1002'
GROUP BY r.`type_id`
It might return something like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
4 | hij | 20
It works. But the issue is, I want to get 0 as value for other types that doesn't have a row in tblRequest. I mean I want the output to be like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
2 | cda | 0
3 | edg | 0
4 | hij | 20
5 | klm | 0
I mean it should get the rows of all types, but 0 as value for those type that doesn't have a row in tblRequest
You could perform the aggregation on tblRequest and only then join it, using a left join to handle missing rows and coalesce to convert the nulls to 0s:
SELECT t.type_id, type_name, COALESCE(sum_duration, 0) AS duration
FROM tblType t
LEFT JOIN (SELECT type_id, SUM(duration) AS sum_duration
FROM tblRequest
WHERE user_id = '1002'
GROUP BY type_id) r ON t.type_id = r.type_id
Select a.type_id, isnull(sum(b.duration), 0)
From tblType a Left Outer Join tblRequest b
ON a.type_id = b.type_id and b.user_id = 1002
Group by a.type_id

Get data from same table

I have one query about fetch the data.
table name:sub_element
se_id | se_name
1 | GHSB
2 | ENGLISH
3 | GUJ
4 | RUSSIAN
5 | FRENCH
6 | S1
7 | S2
8 | S3
table name = class_standard
cs_id | board_se_id | medium_se_id | s_se_id
1 | 1 | 2 | 6,7
2 | 3 | 4 | 6,8
table name:class_standard_subject
css_id | cs_id | se_id
1 | 1 | 6
2 | 1 | 7
3 | 2 | 6
4 | 2 | 8
expected output is:
GHSB | ENGLISH | S1,S2
GUJ | RUSSIAN | S1,S3
Both data are from same table.
How to achieve this type of output.
Please help.
I tried this query but not getting expected output:
select t1.*,a1.* FROM ((
SELECT cs.cs_id, se.se_name as bname FROM sub_element se, class_standard cs WHERE cs.board = se.se_id GROUP BY cs.cs_id) as a1,
(SELECT se.se_name as mname FROM sub_element se, class_standard cs WHERE cs.medium = se.se_id GROUP BY cs.cs_id ) as t1)
You need to join sub_element 2 times as
select
se1.se_name as se_name1,
se2.se_name as se_name2
from class_standard cs
join sub_element se1 on se1.id = cs.board_se_id
join sub_element se2 on se2.id = cs.medium_se_id
UPDATE : With the updated question you have comma separated values and you should avoid these since its not properly normalized and lead into many issues in future.
However you can achieve the result as
select
cs.cs_id,se1.se_name as se_name1,
se2.se_name as se_name2,
group_concat(se3.se_name) as se_name3
from class_standard cs
join sub_element se1 on se1.se_id = cs.board_se_id
join sub_element se2 on se2.se_id = cs.medium_se_id
left join sub_element se3 on find_in_set(se3.se_id,cs.s_se_id)
group by cs.cs_id ;