self join plus another join - mysql

I have a table of country teams
id country group
1 Poland 1
2 Greece 1
3 England 2
4 France 2
5 Germany 3
6 Spain 3
I also a table of scores for each country
fromId score
1 100
3 50
2 90
4 60
What I would like to do is get back a table of scores for each country within a group, having supplied just a country name. For example if I supply "France" then I would want the following table back
country score
England 50
France 60
I have managed to self join the country table with
SELECT
`t1`.`fldCountry`,
`t1`.`fldID`
FROM tblteam t1, tblteam t2
WHERE
t2.fldTeam = t1.fldTeam
AND
t2.fldCountry = 'France'
but am now stuck how to joing this back to get the data!
Please could anyone help a little?

Here you go
Country Table:
CREATE TEMPORARY TABLE Country
(
id INT,
country VARCHAR(20),
grp INT
)
INSERT INTO Country
(
id,
country,
grp
)
SELECT 1,
'Poland',
1
UNION ALL
SELECT 2,
'Greece',
1
UNION ALL
SELECT 3,
'England',
2
UNION ALL
SELECT 4,
'France',
2
UNION ALL
SELECT 5,
'Germany',
3
UNION ALL
SELECT 6,
'Spain',
3
Score Table:
CREATE TEMPORARY TABLE Score ( fromid INT, score INT )
INSERT INTO Score
(
fromid,
score
)
SELECT 1,
100
UNION ALL
SELECT 3,
50
UNION ALL
SELECT 2,
90
UNION ALL
SELECT 4,
60
Query:
SELECT b.country,
IFNULL(s.score, 0) score
FROM Country a
INNER JOIN Country b
ON a.grp = b.grp
LEFT JOIN score s
ON s.fromid = b.id
WHERE a.country = 'France'
Result:
Country Score
England 50
France 60

SELECT
c.country,
s.score
FROM
countries c
INNER JOIN scores s ON c.id = s.fromId
WHERE
c.group = (SELECT group FROM countries WHERE country = 'France' LIMIT 1);

Something like this should work.
Select C2.Country,SUM(S.Score)
FROM Country AS C1
INNER JOIN Country AS C2
ON C1.Group=C2.Group
INNER JOIN Scores AS S
ON C2.id = S.FromID
WHERE C1.Country=#Country
GROUP BY C2.Country
Where #Country is the country you want to look up, in your case, France.

Related

Select users who have won in different categories

I need to select the users who have won at least 1 round in all of the different competitions.
I have the following table sctructure:
results
id
round_id
user_id
result
1
1
2
1
2
1
1
2
rounds
id
category_id
1
1
2
1
3
2
4
2
categories championship
id
competition_id
1
1
2
2
competitions team
id
competition_name
1
Competition A
2
Competition B
Now let's say Bob has won at least 1 round in both Competition A and B each, he needs to show up in the list. But Joe, who's won 1 round in Competition A but nothing in Competition B, must not show up.
I tried writing a script like this, but I can see the flaw in my logic. It's looking for a row where the round_id is both 1 and 2 is impossible.
SELECT user_id FROM results WHERE
(result = 1 AND round_id IN
(SELECT id FROM rounds WHERE category_id IN
(SELECT id FROM categories WHERE competition_id = 7)
)
) AND
(result = 1 AND round_id IN
(SELECT id FROM rounds WHERE category_id IN
(SELECT id FROM categories WHERE competition_id = 8)
)
) AND
(result = 1 AND round_id IN
(SELECT id FROM rounds WHERE category_id IN
(SELECT id FROM categories WHERE competition_id = 9)
)
)
GROUP BY driver_id
How can I achieve this?
Join results to rounds and categories (competitions is not needed because competition_id exists in categories).
Then filter the rows for only the users who won at least one round and aggregate by user with the condition in the HAVING clause for the user that all 3 competition_ids are returned:
SELECT rs.user_id
FROM results rs
INNER JOIN rounds rd ON rd.id = rs.round_id
INNER JOIN categories cg ON cg.id = rd.category_id
WHERE rs.result = 1 AND cg.competition_id IN (7, 8, 9)
GROUP BY rs.user_id
HAVING COUNT(DISTINCT cg.competition_id) = 3;

MYSQL - count how many times a person comes second in a competition

I have the following table called 'players' which contains scores from 3 games played by Tim, Bob and Jon.
playid | name | score
1 | Tim | 10
1 | Bob | 5
2 | Tim | 5
2 | Bob | 10
3 | Tim | 5
3 | Bob | 10
3 | Jon | 4
I want to be able to count the number of times that Tim, Bob and Jon have come second i.e. Tim = 2, Bob = 1, Jon = 0.
I have the following query:
SELECT name FROM players WHERE playid = 1 ORDER BY score Desc LIMIT 1, 1
Which returns the name of the person in second place in the first game i.e. Bob, but I can't figure out how to extend this to cover all games and players. Eventually I also want to be able to count the number of times they come 3rd, 4th etc.
Thanks in advance
Try with following one:
SELECT count(playid), name, score
FROM `players`
WHERE score = (SELECT MAX(score) FROM players WHERE score < (SELECT MAX(score) FROM players))
GROUP BY score, name;
With multiple joins and groupings:
select pl.name, ifnull(counter, 0) counter from (
select distinct name from players) pl
left join (
select players.name, count(*) counter from players
inner join (
select p.playid, max(p.score) as secondscore from (
select players.* from players
left join (
select playid, max(score) as maxscore
from players
group by playid) p
on p.playid = players.playid and p.maxscore = players.score
where p.maxscore is null) p
group by p.playid
) p
on p.playid = players.playid and p.secondscore = players.score
group by players.name) p
on p.name = pl.name
See the demo
You can use this query below to find the secondly-ranked people :
SELECT q2.playid, q2.name
FROM
(
SELECT q1.* , if(switch1,#r:=#r+1,#r:=0) as switch2
FROM
(
SELECT playid, score, name,
if(playid=#p,#p:=0,#p:=playid+1) as switch1
FROM players
JOIN ( SELECT #p:=0, #r:=-1 ) p2
ORDER BY playid, score desc
) q1
) q2
WHERE q2.switch2 = 1
ORDER BY q2.playid
playid name
------ ----
1 Bob
2 Tim
3 Tim
4 George
Rextester Demo
As per the comment by Raymond Nijland, in MySQL 8.0+ you can use window functions to achieve this:
SELECT name, COUNT(*) AS second_place_count
FROM (
SELECT
name,
playid,
ROW_NUMBER() OVER (PARTITION BY playid ORDER BY score DESC) AS rn
FROM players
) AS ranks
WHERE ranks.rn = 2
GROUP BY name
...or if you want to extend this to all places:
SELECT
name,
rn AS place,
COUNT(*) AS place_count
FROM (
SELECT
name,
playid,
ROW_NUMBER() OVER (PARTITION BY playid ORDER BY score DESC) AS rn
FROM players
) AS ranks
GROUP BY
name,
rn

SQL - How to calculate column value and join with another table

As I am not good with MySQL query's so I wish someone help me for creating this kind of sql query.
I having two MySQL tables which is describe bellow:
Table Name: rating
-------------------
property_id user_id area_rate_count safety_rate_count friendly_rate_count walkability_rate_count
4 28 1 1 1 2
5 38 2 3 4 1
5 40 2 2 3 1
6 40 2 3 1 4
10 43 2 2 3 1
Table Name: listing
-------------------
property_id title
4 Sample 1
5 Sample 2
6 Sample 3
10 Sample 4
11 Sample 5
12 Sample 6
Now first I want to sum each column and divide. (area_rate_count, safety_rate_count, friendly_rate_count, walkability_rate_count). For example In property_id:5 having two times so first calculate column sum and divide by 2.
After calculation we will get this output:
Table Name: rating (After Calculation)
--------------------------------------
property_id rate
4 5
5 9 (Divided by 2 because this property_id is two times in table)
6 10
10 8
And Finally I want join this result to my listing table and result looks something like this:
Table Name: listing
-------------------
property_id title rate
4 Sample 1 5
5 Sample 2 9 (Divided by 2 becouse property_id is two times in table)
6 Sample 3 10
10 Sample 4 8
11 Sample 5 0
12 Sample 6 0
Thanks.
I think you want the avg() aggregation function along with a join:
select l.property_id, l.title,
coalesce(avg(area_rate_count + safety_rate_count + friendly_rate_count + walkability_rate_count
), 0) as rate
from listing l left outer join
property_id p
on l.property_id = p.property_id
group by l.property_id, l.title ;
If I understood it right I think you need this:
select l.property_id, l.title, coalesce(r.ssum/if(r.ct=0,1,r.ct), 0) as rate
from listing l LEFT JOIN
(select property_id,
sum(area_rate_count+safety_rate_count
+friendly_rate_count+walkability_rate_count) ssum,
count(*) ct
from rating
group by property_id ) r
ON l.property_id = r.property_id
order by l.property_id
See it here on fiddle: http://sqlfiddle.com/#!2/589d6/5
Edit
As OP asked on the comments that he wants all columns from listing here is what he want:
select l.*, coalesce(r.ssum/if(r.ct=0,1,r.ct), 0) as rate
from listing l LEFT JOIN
(select property_id,
sum(area_rate_count+safety_rate_count
+friendly_rate_count+walkability_rate_count) ssum,
count(*) ct
from rating
group by property_id ) r
ON l.property_id = r.property_id
order by l.property_id
CREATE TEMPORARY TABLE IF NOT EXISTS
temp_table ( INDEX(col_2) )
ENGINE=MyISAM
AS (
SELECT
property_id,
AVG(area_rate_count) as area_rate_count,
AVG(safety_rate_count) as safety_rate_count,
AVG(friendly_rate_count) as friendly_rate_count,
AVG(walkability_rate_count) as walkability_rate_count
FROM rating
GROUP BY property_id
)
SELECT * FROM listing L
JOIN temp_table T
ON L.property_id = T.property_id
Use the below statement to get distinct property_id with its own rate
select property_id, sum(separaterating)/count(property_id) from (
select property_id,sum(area_rate_count , safety_rate_count , friendly_rate_count , walkability_rate_count) as separaterating from rating group by property_id AS temp ) group by
property_id
you can then join with the other table to get the final result as below
select * from ( select property_id, sum(separaterating)/count(property_id) from (
select property_id,sum(area_rate_count , safety_rate_count , friendly_rate_count , walkability_rate_count) as separaterating from rating group by property_id AS temp ) group by
property_id) AS A inner join listing AS B on A.property_id = B.property_id
try this:
select a.prop_id as property_id, l.title, a.allratings / b.numberofreviews as rate
from
(
select property_id as prop_id, SUM(coalesce(area_rate_count,0) + coalesce(safety_rate_count,0) + coalesce(friendly_rate_count,0) + coalesce(walkability_rate_count,0)) as allratings
from rating
group by property_id
) a inner join
(
select property_id, count(distinct user_id) as numberofreviews
from rating
group by property_id
) b on a.property_id = b.property_id
inner join listing l on a.property_id = l.property_id
Try This Query
select ls.property_id,ls.title,inr.rate from listing as ls
left join
(select r.property_id as pid,r.rate/r.cnt as rate from
(select property_id,user_id,(area_rate_count+safefty_rate_count+friendly_rate_count+walkability_rate_count) as rate,count(*) as cnt from rating group by property_id) as r) as inr on inr.pid=ls.property_id

Mysql Join to show all records with or without matching records

I have two tables. One for employees and another for ratings. Some employees have been rated others not. I'm trying to list all the employees with or without a rating value and sort them from the highest rating.
emp table
empid empName
--------------
1 John
2 Alex
3 Peter
4 Mary
Ratings table
ratingid | customerid | ratingvalue | empid
---------------------------------------------
1 1 4 1
2 6 2 1
3 4 3 3
4 5 5 4
Expected output:
empid | empName | avgrating | ratingcount
---------------------------------------------
1 John 3 2
2 Alex 0 0
3 Peter 3 1
4 Mary 5 1
I tried
SELECT *, round(AVG(ratingvalue ), 2) as avgrating, count(empid) as ratingcount
FROM emp LEFT JOIN ratings ON emp.empid = ratings.empid ORDER BY `avgrating`
DESC
That query doesn't produce the result I expect. It only gives me one row.
Thanks in advance
need group by and coalesce null values to 0.
SELECT EmpID, empName,
coalesce(round(AVG(ratingvalue ), 2),0) as avgrating,
coalesce(count(empid),0) as ratingcount
FROM emp
LEFT JOIN ratings
ON emp.empid = ratings.empid
GROUP BY EmpID, EmpName
ORDER BY `avgrating`DESC
I think this is what you want.
select
e.id, e.name,
avg(coalesce(t1.rating,0)) average_rating,
count(coalesce(t1.rating,0)) count_rating
from
(select '1' id, 'John' name union all
select '2' id, 'Alex' name union all
select '3' id, 'Peter' name union all
select '4' id, 'Mary' name) e
left join (select '1' id, '1' customer_id, '4' rating, '1' emp_id union all
select '2' id, '6' customer_id, '2' rating, '1' emp_id union all
select '3' id, '4' customer_id, '3' rating, '3' emp_id union all
select '4' id, '5' customer_id, '5' rating, '4' emp_id) t1 on
t1.emp_id = e.id
group by
e.id,
e.name
I have just subbed out your tables for data. You can run this in mysql directly and it should give you want you want.

Mysql - Return rows that have no associated records in another table plus max that do have records

A simplified version of my problem. I have 2 tables:
Scores:
id code_id score
1 11 100
2 12 20
3 13 40
4 14 70
5 15 90
6 16 10
7 17 30
8 18 50
Codes:
id code
11
12
13
14
15
16 BBB
17 BBB
18 BBB
I need to produce a Mysql SELECT query that would return all the rows from the Scores table that have no associated codes in the Codes table plus just the highest score from rows that do have identical codes in the Codes table.
The required output is show below. Score ID's 1-5 are present as they have no associated code but only ID 8 is selected because it is the highest value of all the scores with a BBB code.
id code_id score
1 11 100
2 12 20
3 13 40
4 14 70
5 15 90
8 18 50
I hope that makes sense. I thought this would be an easy one but It has me puzzled. I have checked out a load of the [greatest-n-per-group] tagged questions and most seem to only reference one table when performing GROUP BYs and subselects and have not helped.
Many thanks.
Maybe I am missing something with your requirements but it might be easier to use a UNION ALL between two queries.
To get the rows that have a null code in the codes table you can use:
select s.id, s.code_id, s.score
from scores s
where exists (select id
from codes c
where s.code_id = c.id
and c.code is null)
Then to get the max(score) for each code, you can use:
select s.id, s.code_id, s.score
from scores s
inner join codes c
on s.code_id = c.id
inner join
(
select c.code, max(s.score) score
from scores s
inner join codes c
on s.code_id = c.id
where c.code is not null
group by c.code
) m
on c.code = m.code
and s.score = m.score;
Finally you can use a UNION ALL to combined the two queries:
select s.id, s.code_id, s.score
from scores s
where exists (select id
from codes c
where s.code_id = c.id
and c.code is null)
union all
select s.id, s.code_id, s.score
from scores s
inner join codes c
on s.code_id = c.id
inner join
(
select c.code, max(s.score) score
from scores s
inner join codes c
on s.code_id = c.id
where c.code is not null
group by c.code
) m
on c.code = m.code
and s.score = m.score
group by c.code //To remove duplicates where the code and the score are equal
See SQL Fiddle with Demo.
Edit as a side note if the code value is an empty string and not null, then you can alter the code to use '' (see demo).
Try this:
select s.*
from scores s join
(select coalesce(code, cast(id as varchar(32))) as codeid, max(id) as id
from codes c
group by coalesce(code, cast(id as varchar(32)))
) c
on s.code_id = c.id;
The idea is to summarize the codes table to get the maximum id per code, as well as a single row for each id where the code is NULL.
The following shows the results (apologies, this is for SQL Server but the code would be quite similar there):
declare #scores table (id int identity(1, 1), code_id int, score int);
insert into #scores(code_id, score)
select 11, 100 union all
select 12, 20 union all
select 13, 40 union all
select 14, 70 union all
select 15, 90 union all
select 16, 30 union all
select 17, 30 union all
select 18, 50;
declare #codes table (id int, code varchar(3));
insert into #codes(id, code)
select 11, NULL union all
select 12, NULL union all
select 13, NULL union all
select 14, NULL union all
select 15, NULL union all
select 16, 'BBB' union all
select 17, 'BBB' union all
select 18, 'BBB';
select s.*
from #scores s join
(select coalesce(code, cast(id as varchar(32))) as codeid, max(id) as id
from #codes c
group by coalesce(code, cast(id as varchar(32)))
) c
on s.code_id = c.id