I have a query:
SELECT p.NAME, r.DATE, c.NAME, SUM(hs.result), SUM(h.par), (SUM(hs.result) - SUM(h.par)) AS "Score"
FROM hole_scores hs
JOIN players p ON hs.player_id = p.id
JOIN rounds r ON r.id = hs.session_id
JOIN holes h ON h.id = hs.hole_id
JOIN courses c ON c.id = r.course_id
GROUP BY p.NAME, r.id
ORDER BY Score ASC
The query gives me this result (first rows):
NAME DATE NAME SUM(hs.result) SUM(h.par) Score
Player 1 29.7.2014 Course 1 50 57 -7
Player 2 7.6.2014 Course 2 48 54 -6
Player 1 22.5.2014 Course 1 51 57 -6
Player 3 6.6.2014 Course 1 52 57 -5
Player 1 19.8.2013 Course 1 53 57 -4
Player 4 1.9.2011 Course 1 56 59 -3
Player 5 15.10.2011 Course 1 56 59 -3
Player 4 2.8.2013 Course 1 54 57 -3
Player 1 3.6.2014 Course 1 54 57 -3
Player 6 8.7.2014 Course 1 54 57 -3
Player 4 16.7.2014 Course 1 54 57 -3
Player 1 21.4.2015 Course 1 53 56 -3
Player 2 23.6.2012 Course 2 52 54 -2
How would I get average of each player scores?
Please try the following...
SELECT players.id AS player_id,
players.name AS player_name,
AVG( Score ) AS avg_score
FROM ( SELECT hole_scores.player_id AS player_id,
( SUM( hole_scores.result ) - SUM( holes.par ) ) AS Score
FROM hole_scores
JOIN holes ON holes.id = hole_scores.hole_id
JOIN rounds ON rounds.id = hole_scores.session_id
GROUP BY hole_scores.player_id,
rounds.id
) AS scoreFinder
JOIN players ON players.id = scoreFinder.player_id
GROUP BY players.id
ORDER BY player_name,
player_id;
This statement uses a subquery based on a reduced version of your supplied statement to find the scores for each player for each round.
Note that normally name can not be safely assumed to be unique as it is normally possible, even if it is not normally the case, that two players can have the same name. As such I have chosen the id value to uniquely identify each player. Since this value can be found in hole_scores.player_id there is no need to join with players at this stage.
It then performs an INNER JOIN between the subquery's results and players so that the name of each player may be included in the results. I chose to join with players at this stage rather than in the subquery as the results of the subquery will have only one record for each player to join to rather than potentially many for each player in the subquery's source dataset. I hope that this will make the statement slightly more efficient.
The statement then groups the joined dataset's records by the unique identifier for each player, and returns the id and name values for each player along with the average value of their Score at the end of each round.
If you have any questions or comments, then please feel free to post a Comment accordingly.
If you want the average, I think you can do:
SELECT p.NAME, (SUM(hs.result) - SUM(h.par))/COUNT(*) AS "Score"
FROM hole_scores hs JOIN
players p
ON hs.player_id = p.id JOIN
rounds r
ON r.id = hs.session_id JOIN
holes h
ON h.id = hs.hole_id JOIN
courses c
ON c.id = r.course_id
GROUP BY p.NAME
ORDER BY Score ASC
Related
I looked around for answers to this question but all the ones I tried simply didn't work. The other answer suggestions all threw errors for me. Maybe it's because I'm using using MariaDB ?.
SELECT * FROM 'view_winners'
I need top 3 in column 'class'
table view_winners is multiple left joins and I could not figure out how to limit 3 of the left join on table allClasses.
view_winners is:
`$view = "view_winners";
$db->query("DROP $view");
$db->query("CREATE VIEW $view AS
SELECT *
FROM thw22 evnt
LEFT JOIN allUsers usr
ON usr.user_id = evnt.e_owner_id
LEFT JOIN hw_vehicles veh
ON veh.vehicle_id = evnt.e_vehicle_id
LEFT JOIN hw_m_vehicle_class mcls
ON mcls.v_class_id = evnt.e_class_id
LEFT JOIN allClasses cls
ON mcls.cvm_id = cls.class_id
LEFT JOIN hw_v_scores sco
ON sco.v_score_id = evnt.e_score_id
WHERE (cls.class_name <> '' OR cls.class_name IS NOT NULL)
AND (sco.total <> '' OR sco.total IS NOT NULL)
ORDER BY cls.vehicle_type ASC, cls.class_name ASC, sco.total DESC
");`
It's probably best if I could LIMIT 3 on LEFT JOIN allClasses but I can't figure that out. So I figured I would loop through the result and unset rows over 3 in class in PHP. But again I could not figure out how to compare rows as looping through.
I need help with the LIMIT 3 on the JOIN or how to compare the results unsetting rows.
entry
class
score
786
sally
99
234
sally
90
456
bob
45
621
joe
90
964
joe
80
548
joe
66
346
joe
22
900
frank
89
700
frank
86
800
frank
72
123
frank
70
860
frank
50
333
frank
45
Desired results:
entry
class
score
786
sally
99
234
sally
90
456
bob
45
621
joe
90
964
joe
80
548
joe
66
900
frank
89
700
frank
86
800
frank
72
Might this answer help
And to clarify, it appears that for example, you want AT MOST, 3 entries per class (person name per sample data). If one class has only a single entry, get it. However, if someone else has 8 classes you want only the first 3 based on some pre-determined priority ordering, such as top 3 scores.
In your case, the OVER is partitioned by the "class", and the order by will be the score DESC (descending). So having the view give you this extra computed column (per class), you can then filter WHERE finalColumnNameYouAssign <= 3
I just answered similar question yesterday. See How to limit SQL query with JOIN
Here's my solution based on 2 linked tables, users and history for each user. There is also another solution there depending on your MySQL version.
SELECT *
FROM
`users` AS u
LEFT JOIN `history` AS h ON u.id = h.user_id
WHERE
FIND_IN_SET(h.id, (SELECT `list` FROM
(SELECT user_id, SUBSTRING_INDEX(GROUP_CONCAT(id SEPARATOR ','), ',', 3) AS `list` FROM
(SELECT h.user_id, h.id
FROM
`users` AS u
LEFT JOIN `history` AS h ON u.id = h.user_id
) AS `a`
GROUP BY user_id
) AS `b`
WHERE b.user_id = u.id
) )
Instead of having so many joins and confusing myself badly, I made a couple different views that I will need anyway for other statistics. Then by simply ordering by class ASC, score DESC I have a very simple master list of all classes in order then scores highest to lowest (along with all the other joined data). After that I can compare each row and limit 3 as follows:
SELECT * FROM
(SELECT *,
#rn := IF(#prev = class_name,
#rn + 1, 1)
AS rn,
#prev := class_name
FROM view_allScores
JOIN (SELECT #prev := NULL, #rn := 0)
AS vars
) AS T1
WHERE T1.rn <= 3
The confusing thing was that I was trying to add a LIMIT to a join and that kept confusing me.
I have a Mysql table with users playing as double partners in a tennis tournament and also a table with rankings in which every player has a ranking score in doubles matches.
Tournaments_registrations_doubles
id
player1_id
player2_id
1
32
25
2
25
28
3
143
83
Rankings_doubles
id
player_id
points
1
25
127
2
19
83
3
32
97
4
83
41
I am trying to build an SQL query that will get the ids of the Tournaments_registrations_doubles table in descending order based on the sum of the ranking points of each player group. So player_1 and player_2 who's sum of point in rankings table is highest should come first, and so on. Not all players are registered in the ranking table.
Any ideas on how to implement this?
Join the tournament table to the doubles table twice, once for each of the two player columns. Then order the result set descending by the sum of points.
SELECT t.id, t.player1_id, t.player2_id,
COALESCE(r1.points, 0) + COALESCE(r2.points, 0) AS total
FROM Tournaments_registrations_doubles t
LEFT JOIN Rankings_doubles r1 ON r1.player_id = t.player1_id
LEFT JOIN Rankings_doubles r2 ON r2.player_id = t.player2_id
ORDER BY COALESCE(r1.points, 0) + COALESCE(r2.points, 0) DESC;
Notice that we use left joins here, so that we may treat missing points as zero when computing the totals.
I have been trying to solve a problem for a very long time- days- and I am not making any progress. Basically, I have two tables, players and matches. Each player in players has a unique player_id, as well as a group_id that identifies which group he/she belongs to. Each match in matches has the player_ids of two players in it, first_player and second_player, who are always from the same group. first_score corresponds to the score that first_player scores and second_score corresponds to the score that second_player scores. A match is won by who ever scores more. Here are the two tables:
create table players (
player_id integer not null unique,
group_id integer not null
);
create table matches (
match_id integer not null unique,
first_player integer not null,
second_player integer not null,
first_score integer not null,
second_score integer not null
);
Now what I am trying to do is to get the players with the most wins from each group, their group ID as well as the number of wins. So, for example, if there are three groups, the result would be something like:
Group Player Wins
1 24 23
2 13 25
3 34 20
Here's what I have right now
SELECT p1.group_id AS Group, p1.player_id AS Player, COUNT(*) AS Wins
FROM players p1, matches m1
WHERE (m1.first_player = p1.player_id AND m1.first_score > m1.second_score)
OR (m1.second_player = p1.player_id AND m1.second_score > m1.first_score)
GROUP BY p1.group_id
HAVING COUNT(*) >= (
SELECT COUNT(*)
FROM players p2, matches m2
WHERE p2.group_id = p1.group_id AND
((m2.first_player = p2.player_id AND m2.first_score > m2.second_score)
OR (m2.second_player = p2.player_id AND m2.second_score > m2.first_score))
)
My idea is to only select players whose wins are greater than, or equal to, the wins of all other players in his group. There is some syntactic problem with my query. I think I am using GROUP BY incorrectly as well.
There is also the issue of a tie in the number of wins, where I should just get the player with the least player_id. But I haven't even gotten to that point yet. I would really appreciate your help, thanks!
EDIT 1
I have a few sample data that I am running my query against.
SELECT * FROM players gives me this:
Player_ID Group_ID
100 1
200 1
300 1
400 2
500 2
600 3
700 3
SELECT * FROM matches gives me this:
match_id first_player second_player first_score second_score
1 100 200 10 20
2 200 300 30 20
3 400 500 30 10
4 500 400 20 20
5 600 700 20 10
So, the query should return:
Group Player Wins
1 200 2
2 400 1
3 600 1
Running the query as is returns the following error:
ERROR: column "p1.player_id" must appear in the GROUP BY clause or be used in an aggregate function
Now I understand that I have to specify player_id in the GROUP BY clause if I want to use it in the SELECT (or HAVING) statement, but I do not wish to group by player ID, only by the group ID.
Even if I do add p1.player_id to GROUP BY in my outer query, I get...the correct answer actually. But I am a bit confused. Doesn't Group By aggregate the table according to that column? Logically speaking, I only want to group by p1.group_id.
Also, if I were to have multiple players in a group with the highest number of wins, how can I just keep the one with the lowest player_id?
Edit 2
If I change the matches table to such that for Group 1, there are two players with 1 win each, the query result omits Group 1 from the result altogether.
So, if my matches table is:
match_id first_player second_player first_score second_score
1 100 200 10 20
2 200 300 10* 20
3 400 500 30 10
4 500 400 20 20
5 600 700 20 10
I would expect the result to be
Group Player Wins
1 200 1
1 300 1
2 400 1
3 600 1
However, I get the following:
Group Player Wins
2 400 1
3 600 1
Note that the desired result is
Group Player Wins
1 200 1
2 400 1
3 600 1
Since I wish to only take the player with the least player_id in the case of a draw.
WITH first_players AS (
SELECT group_id,player_id,SUM(first_score) AS scores FROM players p LEFT JOIN matches m ON p.player_id=m.first_player GROUP BY group_id,player_id
),
second_players AS (
SELECT group_id,player_id,SUM(second_score) AS scores FROM players p LEFT JOIN matches m ON p.player_id=m.second_player GROUP BY group_id,player_id
),
all_players AS (
WITH al AS (
SELECT group_id, player_id, scores FROM first_players
UNION ALL
SELECT group_id, player_id, scores FROM second_players
)
SELECT group_id, player_id,COALESCE(SUM(scores),0) AS scores FROM al GROUP BY group_id, player_id
),
players_rank AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY group_id ORDER BY scores DESC, player_id ASC) AS score_rank,
ROW_NUMBER() OVER(PARTITION BY scores ORDER BY player_id ASC) AS id_rank FROM all_players ORDER BY group_id
)
SELECT group_id, player_id AS winner_id FROM players_rank WHERE score_rank=1 AND id_rank=1
Results
group_id winner_id
1 45
2 20
3 40
Try it Out
try like below
with cte as
(
select p.Group_ID,t1.winplayer,t1.numberofwin
row_number()over(partition by p.Group_ID order by t1.numberofwin desc,t1.winplayer) rn from players p join
(
SELECT count(*) as numberofwin,
case when first_score >second_score then first_player
else second_player end as winplayer
FROM matches group by case when first_score >second_score then first_player
else second_player end
) t1 on p.Player_ID =t1.winplayer
) select * from cte where rn=1
It works when you add the player_id in the GROUP BY because you know each player plays only in one group. So you group by the player in a certain group. That is why, logically, you can add the player_id to the GROUP BY.
I'm trying to get the total number of levels gained or lost from this sort of table:
id name level timestamp
1 Rex 15 10:25
2 Rex 15 10:26
3 Rex 15 10:27
4 Rex 14 10:28
5 Rex 13 10:29
6 Rex 13 10:30
7 Rex 13 10:31
8 Rex 13 10:29
9 Xer 44 10:30
10 Xer 44 10:31
11 Xer 45 10:32
12 Xer 45 10:33
13 Xer 45 10:34
Currently I'm running
SELECT id, name, level, timestamp, MAX(level) - MIN(level) AS gained
FROM log
GROUP BY name
But the problem with this query is that both gained and lost levels will count as gained. It would be perfect if I could get a negative int in the gained column if the user has lost levels
The output I want from the data above is:
id name level timestamp gained
8 Rex 13 10:29 -2
13 Xer 45 10:34 1
If you need to respect the timeline, then try something like this:
SELECT MAX(id) id, name,
( SELECT level FROM log l0 WHERE l.name = l0.name ORDER BY timestamp DESC LIMIT 1 ) level,
MAX(timestamp) timestamp,
-- last entry for the name
( SELECT level FROM log l1 WHERE l.name = l1.name ORDER BY timestamp DESC LIMIT 1 ) -
-- first entry for the name
( SELECT level FROM log l2 WHERE l.name = l2.name ORDER BY timestamp ASC LIMIT 1 ) gained
FROM log l
GROUP BY name
I used LAG in as subquery to get the changes and then summed those changes in an outer sub-query. To get the last row I uses yet another query to find the max time for each name. Maybe not the most efficient query but it works
SELECT l.id, l.name, l.level, l.timestamp, sg.gain
FROM log l
JOIN (SELECT name, SUM(gain) gain
FROM (SELECT name, level - COALESCE(LAG(level) OVER w, level) as gain
FROM log
WINDOW w AS (PARTITION BY name ORDER BY timestamp)) as g
GROUP BY name) as sg ON sg.name = l.name
JOIN (SELECT name, MAX(time) max_t
FROM log
GROUP BY name) mt ON mt.name = l.name AND mt.max_t = l.time
I am writing a hockey stat database. I have the need to calculate how many players were on the ice when a goal is scored.
I have set up the following sqlfiddle:
http://sqlfiddle.com/#!9/ed8fa
problem I am having is how to filter the results such that only goals that are scored at regular strength (shot type "reg") or goals that are scored while shorthanded (shot type "sh") count towards this total.
I need all players to be listed in the output, even if their number is zero. After finding the players "Plus rating", I will calculate each players "minus rating" and subtract the two. Once I figure out how to accurately find the "Plus rating", I should be able to figure out how to do the rest.
For the given data in the sqlfiddle, the desired output should be:
player_id Plus_count
20 1
21 0
22 2
23 1
24 1
25 0
26 1
27 1
28 1
29 0
30 0
31 2
32 1
33 0
I am having trouble figuring out how the join would work such that I can use where to filter out all goals that are "pp".
Thanks for any pointers you might be able to give...
adding what I have tried. This is from my dev db, so just slightly different from the sqlfiddle:
SELECT players.player_id as pp_id,
COALESCE(plus_players.count, 0) as Plus_Count
FROM players
LEFT JOIN (SELECT plus_players.fk_player_id, COUNT(*) as count
FROM plus_players
GROUP BY plus_players.fk_player_id) plus_players
ON plus_players.fk_player_id = players.player_id
left join (select * from shots_for
where regular_powerplay_shorthanded = 'reg' or regular_powerplay_shorthanded = 'sh') sf on sf.fk_player_id = players.player_id
union
SELECT players.player_id as pp_id,
COALESCE(plus_players.count, 0) as Plus_Count
FROM players
LEFT JOIN (SELECT plus_players.fk_player_id, COUNT(*) as count
FROM plus_players
GROUP BY plus_players.fk_player_id) plus_players
ON plus_players.fk_player_id = players.player_id
right join (select * from shots_for
where regular_powerplay_shorthanded = 'reg' or regular_powerplay_shorthanded = 'sh') sf2 on sf2.fk_player_id = players.player_id
left join goals_for on goals_for.fk_shot_for_id = sf2.shot_for_id
where regular_powerplay_shorthanded = 'reg' or regular_powerplay_shorthanded = 'sh'
you can see my various attempts above to use the where to eliminate the "pp" goals.