SQL query to order by the top averages of previous data - mysql

Given a table with data with these columns (other columns too, but, these are the important ones):
gameid awayscore homescore
1 3 8
2 4 2
3 4 9
4 15 5
5 0 8
6 4 10
...
What I am looking for is to get the average margin of victory over the previous 3 games (assuming gameid is the order they were played), so, on game 6, it should be (6+8+10=24, average 8), on game 5 (8+10+5=23, average 7.666), etc. And then show the weeks where the 3-game average was the highest.
I tried to do it like this:
SELECT g.gameid,
(SELECT AVG(scores.ave) FROM (SELECT ABS(awayscore-homescore) ave FROM games gs
WHERE (gs.gameid<=g.gameid)
ORDER BY gameid DESC LIMIT 3) scores) margin
FROM `games` g
GROUP BY g.gameid
ORDER BY margin DESC
LIMIT 10;
But I get "Unknown column 'g.gameid' in 'where clause'", I assume because it is a subquery of a subquery(?), and it loses the reference? I am at a loss of how to structure it otherwise though to work correctly (if it is possible at all).

try this query
SELECT g.gameid,
(SELECT AVG(ABS(awayscore-homescore))
FROM games gs
WHERE (gs.gameid<=g.gameid)
AND (gs.gameid>g.gameid-3)
) margin
FROM `games` g
ORDER BY margin DESC
LIMIT 10;
sqlfiddle
UPDATE if you want to use gamedate (datetime) to order your game, you can create a game order (gorder) number based on this sort for everytime you select from games like the below query.
SELECT g.gameid,
(SELECT AVG(ABS(awayscore-homescore))
FROM
(SELECT awayscore,homescore,#rank:=#rank+1 as gorder
FROM games,(SELECT #rank:=0)r
ORDER BY gamedate ASC
)gs
WHERE (gs.gorder <= g.gorder)
AND (gs.gorder > g.gorder-3)
) margin
FROM (SELECT games.gameid,#rank2:=#rank2+1 as gorder
FROM games,(SELECT #rank2:=0)r2
ORDER BY gamedate ASC) g
ORDER BY margin DESC
LIMIT 10;
sqlfiddle

Related

Get distinct values from groups in MySQL

I want to get the id of the lowest points from each team (the team field).
My query works but i need to make sure the following query is good enough with a large table.
I need Simplification and Optimization.
Query:
SELECT T.id from teams as T
INNER JOIN (
SELECT MIN(T1.points) AS P FROM teams AS T1
GROUP BY T1.team LIMIT 5
) TJOIN ON T.points IN (TJOIN.P)
GROUP BY T.team
ORDER BY T.points ASC LIMIT 5
Table teams
id
team (foreign_key)
points (indexed)
1
a
100
2
a
101
3
b
106
4
c
105
5
c
102
Result
id
1
5
3
I believe the query you are looking for is:
SELECT MIN(T.id)
FROM teams as T
INNER JOIN (
SELECT team, MIN(points) AS min_points
FROM teams
GROUP BY team LIMIT 5
) TJOIN
ON T.team = TJOIN.team
AND T.points = TJOIN.min_points
GROUP BY T.team
ORDER BY T.points ASC
LIMIT 5
You need to join based on both the column being grouped by and the min value. Consider the result of your query if multiple teams had a score of 100.
Another way of doing this is to use ROW_NUMBER():
SELECT id
FROM (
SELECT id, points, ROW_NUMBER() OVER (PARTITION BY team ORDER BY points ASC, id ASC) rn
FROM teams
) t
WHERE rn = 1
ORDER BY points ASC
LIMIT 5

Sum Top 10 Values

I’ve searched and I know this has been asked before but I am struggling to get my head around what I can / can’t do.
My cycling club records race results each time a rider has entered a race. Each result is awarded points - 50 for 1st, 49 for 2nd etc.
So the table looks like
resultid(pk) | riderid(fk) | leaguepts
1 1 50
2 2 49
3 3 48
4 1 50
5 2 42
6 3 50
7 4 30
...etc
I am trying to extract the sum of top 10 points awarded for each riderid from the results table.
(the actual database is a bit more complicated with a table for rider name / rider id and also a race table so we can display the results of each race etc but I just want to get the basic league table query working first of all)
So I want to extract the sum of the top 10 best scores for each rider. Then display each riders score, in a descending league table.
So far I’ve only had success using UNION ALL e.g.
SELECT sum(points) AS pts from
(
SELECT points from `results`
WHERE riderid = 1
ORDER BY points DESC
LIMIT 10
) as riderpts
UNION ALL
SELECT sum(points) AS pts from
(
SELECT points from `results`
WHERE riderid = 2
ORDER BY points DESC
LIMIT 10
) as riderpts
ORDER BY pts DESC
But there could be up to 90-odd riders who have registered at least one score so this query could get very big.
I found this which looks like it should work for me but doesn't. Sum top 5 values in MySQL I changed the column names for my table but it seems to sum all results, not the top 10 for each rider.
Alternatively I could just issue a query for each rider id. Not good I guess?
Subquerying is a problem because I can't limit on the inner query?
Run a job (manual or cron) to update the league table periodically and just display the table results?
Edit (not sure if this is the correct etiquette or I should start a new thread?). Gordon answered the question below but in the meantime I tried to work this out for myself using one of the links below. I could get results that returned the top 10 scores for each rider with the query below
set #riderid = '';
set #riderrow = 1;
select riderid, leaguepts, row_number
from
(
select
riderid,
leaguepts,
#riderrow := if(#riderid = riderid, #riderrow + 1, 1) as row_number,
#riderid := riderid as dummy
from wp_tt_results order by riderid, leaguepts desc
) as x where x.row_number <= 10;
BUT I can't see what I would need to do next to get the sum of top 10 results per riderid?
In MySQL, the easiest way to do this is probably to use variables:
SELECT riderid, sum(points)
FROM (SELECT r.*,
(#rn := if(#r = riderid, #rn + 1,
if(#r := riderid, 1, 1)
)
) as seqnum
FROM results r CROSS JOIN
(SELECT #r := 0, #rn := 0) as wnw
ORDER BY riderid, points DESC
) r
WHERE seqnum <= 10
GROUP BY riderid;

SQL query to return the three highest values for one column "grouped" by another column

Let's say I have a table like this:
Player Score
A 5
B 4
A 3
B 2
A 1
B 1
A 2
B 3
A 4
B 5
I need an SQL query that will return the three highest scores per player in descending order "grouped" by player i.e.
Player Score
A 5
A 4
A 3
B 5
B 4
B 3
Very grateful for any pointers.
This is old-fashioned (read: basic sql) way of producing top-n per group. You might join the table to itself on group condition (here it is player) and pick records with higher score on right side; if there are three or less such records, the row is one of top n rows per group.
select player.player, player.score
from Player
left join Player p2
on p2.player = player.player
and p2.score > player.score
group by player.player, player.score
having count(distinct p2.score) < 3
order by 1, 2 desc
Alternative version you might check, using not exists:
select player, score
from player
where not exists
(
select p2.player
from Player p2
where p2.player = player.player
and p2.score > player.score
group by p2.player
having count(distinct p2.score) > 3
)
order by 1, 2 desc
This two versions differ in presentation of ties - while first one returns one row (by nature of group by) and needs to be joined back to original table to show all records, second one works directly from original table showing all data and ties at once.
You can find Demo at Sql Fiddle.
in SQL server:
select p.player, p.score
from PS p
where p.score in (select top 3 score from PS
where player = p.player order by score desc)
order by p.player asc, p.score desc
in MySql:
select p.player, p.score
from PS p
where p.score in (select score from PS
where player = p.player order by score desc limit 3)
order by p.player asc, p.score desc
I think what you are looking for can be found here:
http://www.sql-ex.ru/help/select16.php
Basically, the best solution uses the RANK function. Here is the example code from the site:
SELECT maker, model, type FROM
(
SELECT maker, model, type, RANK() OVER(PARTITION BY type ORDER BY model) num
FROM Product
) X
WHERE num <= 3
You would just need to modify the Partition By section to order by your score in descending order.
EDIT
Based upon the information that you will be using MySQL, you will need to make some modifications to the above query (which works with Microsoft SQL). You need to replace the RANK function with your own RANK implementation. It isn't that hard. Complete instructions can be found here:
http://thinkdiff.net/mysql/how-to-get-rank-using-mysql-query/
That will show you how to implement a counter that can give you a rank number.
Depending on what DBMS you use, you may be able to use row_number in some form
In SQL Server 2008 you can use
create table #player
( Player char, Score int )
insert into #player (Player, Score) Values
('A',5),('B',4),('A',3),('B',2),('A',1),('B',1),('A',2),('B',3),('A',4),('B',5)
select * from #player
select Player, Score from
(
select *, ROW_NUMBER() over(partition by Player order by Score desc) as rowNo
from #player
) as tmp
where tmp.rowNo <= 3
drop table #player

Order MySQL table by two columns of equal importance

I have a two player game which stores scores in a table.
I want to select high scores from the table, this is my current code:
SELECT * FROM games
ORDER BY GREATEST(player1Score,player2Score) DESC
LIMIT 10
The problem is it only returns one instance of each row, even if for example the lower of the two scores in row 1 warrants inclusion in the top 10.
Use UNION ALL:
SELECT col1, col2, col3, player1Score AS score FROM games
UNION ALL
SELECT col1, col2, col3, player2Score AS score FROM games
ORDER BY score DESC
LIMIT 10
Also, don't use SELECT *. List the columns explicitly.
( SELECT *, player1Score as score
FROM games
ORDER BY score DESC
LIMIT 10
)
UNION ALL
( SELECT *, player2Score AS score
FROM games
ORDER BY score DESC
LIMIT 10
)
ORDER BY score DESC
LIMIT 10
You would probably be better off creating a players table and a join table (even though there are only two users). Then you could easily create a query to do what you're trying to do. Of course, it will take a little bit to alter your update/save functions to match the new schema.
players
-----
playerId
playerName
joinPlayerGame
-----
joinId
playerId
gameId
games
-----
modify the player score fields to just be 'score'
SELECT g.*, p.playerName FROM players p INNER JOIN joinPlayerGame j ON j.playerId = p.playerId INNER JOIN games g ON g.<whatever your key is> = j.gameId ORDER BY g.score DESC LIMIT 10
I hope that helps, good luck!

to get top 5 rankings from database

In my table i have team and points column and I want to get top 5 teams .Teams with same points should be grouped and consider as one of the ranks so if 5 teams are having same points then all should come as one of the rank and next suceeding records according to team points
TRY
SELECT DISTINCT(point), team
FROM tableTeam
ORDER BY points DESC LIMIT 5
SELECT team,
points,
(SELECT COUNT(*)
FROM teams t2
WHERE t2.points > t1.points) + 1 rank
FROM teams t1
ORDER BY points DESC
LIMIT 5
There's no window functions in MySQL, so you'll want to extract the rank in your scripts.
Also, if I'm making sense of your ranking criteria, you're actually interested in getting the top 5 teams plus any additional teams that might have the same number of points as that in the 5th rank.
If so, your limit should be applied to a subquery on the point criteria:
select name, points
from teams
where points >= (
select points
from teams
order by points desc
limit 1 offset 4
)
order by points desc, name
If not, a simple order by/limit will do:
select name, points
from teams
order by points desc, name
limit 5