MySql update order/rank colum but allow for duplicate ranks - mysql

I have a table with game scores for each team like this:
id game_id game_rank score team_id
-------------------------------------------
5 1 0 15 1
4 1 0 25 2
1 1 0 40 3
3 1 0 40 4
2 1 0 55 5
7 2 0 0 1
6 2 0 0 2
I want to automatically sort the teams by score and assign them a game rank (first place, second place...) according to those scores. I would like to allow for ties based on duplicate scores, and only increment the rank if the score is different.
This is my desired result.
id game_id game_rank score team_id
-------------------------------------------
5 1 1 15 1
4 1 2 25 2
1 1 3 40 3
3 1 3 40 4
2 1 4 55 5
7 2 0 0 1
6 2 0 0 2
I have the following query so far, but it does not allow for duplicate game ranks.
SET #lastscore = 0;
SET #ordering = 0;
UPDATE game_scores SET game_rank = (#ordering := #ordering + 1)
WHERE game_id = 1
ORDER BY score;
Can anyone help me handle the duplicate scores?

SET #lastscore = 0;
SET #ordering = 0;
UPDATE game_scores
SET
game_rank = IF(score = #lastscore, #lastscore, (#ordering := #ordering + 1))
, score = (#lastscore := score)
WHERE game_id = 1
ORDER BY score;
You also can do it in one query:
UPDATE game_scores
CROSS JOIN ( SELECT #lastscore:=0, #ordering:=0) AS parameter
SET
game_rank = IF(score = #lastscore, #lastscore, (#ordering := #ordering + 1))
, score = (#lastscore := score)
WHERE game_id = 1
ORDER BY score;

you should check the condition every time when score are not same with previous score then #ordering increase by 1 other wise #ordering same as current #ordering
SET #lastscore := 0;
SET #ordering := 0;
UPDATE game_scores SET
IF(#lastscore = score, #ordering, #ordering := #ordering + 1),
game_rank = #ordering,
#lastscore := score
WHERE game_id = 1
ORDER BY score;

Related

Create a Cumulative Sum Column in MySQL Based On an ID

I have a "simplified" table that looks like this:
player round point
1 1 25
2 1 18
3 1 15
1 2 18
2 2 25
3 2 15
I wanna create a view that calculates pointTot cumulatively based upon plrID
plrID rndID pnt [pointTot]
1 1 25 25
2 1 18 18
3 1 15 15
1 2 18 43
2 2 25 43
3 2 15 30
I've been playing around with different methods for the last few hours.
I would need a variable var based upon the plrID
This is as far as I got without being able to work out how to create a
#psum[#plrID]
set #psum := 0;
select `plrID`, `rndID`, `pnt`, (#psum := #psum + `pnt`) as `pointTot`
from `table`
order by `plrID`;
You can do this using below query
select t.plrID,t.rndID,t.pnt,sum(t1.pnt)
from table t
join table t1
on t.plrID = t1.plrID
and t1.rndID<=t.rndID
group by plrID,rndID
You can do this as:
select `plrID`, `rndID`, `pnt`,
(#psum := if(#p = plrId, #psum + pnt,
if(#p := plrId, pnt, pnt)
)
) as pointTot
from `table` cross join
(select #psum := 0, #p := -1) param
order by `plrID`, rndID;
You cannot add this as a view, because variables are not allowed in a view. You can use this version:
select `plrID`, `rndID`, `pnt`,
(select sum(t2.pnt)
from `table` t2
where t2.plrId = t.plrId and t2.rndId <= t.rndId
) as pointTot
from `table` t ;

MYSQL query better way to group by if values?

I have a working query, but there has to be a better way to do this.
Here is the working query
SELECT gameid FROM(
SELECT gameid, SUM(count) as total FROM (
SELECT IF(vanguard = 1, 30, gameid) as gameid, count FROM (
SELECT opserv_operation.gameid, opserv_games.vanguard, COUNT(opserv_games.gameid) AS count FROM opserv_operation_attendees INNER JOIN opserv_operation ON opserv_operation_attendees.operationid = opserv_operation.operationid INNER JOIN opserv_games on opserv_operation.gameid = opserv_games.gameid WHERE (start_time >= '2015-11-11' || FIND_IN_SET(opserv_operation.operationid, '17951,17701,17702,17775,17969,17890,17958,17966,17900')) AND completed = 1 AND opserv_operation_attendees.userid = 5750 AND opserv_operation_attendees.status = 4 AND opserv_operation.type <> 5 AND opserv_operation.completed = 1 GROUP BY opserv_operation.gameid) as m
) as l
GROUP BY gameid
ORDER BY total DESC LIMIT 1) as k
The main query
SELECT opserv_operation.gameid, opserv_games.vanguard, COUNT(opserv_games.gameid) AS count FROM opserv_operation_attendees INNER JOIN opserv_operation ON opserv_operation_attendees.operationid = opserv_operation.operationid INNER JOIN opserv_games on opserv_operation.gameid = opserv_games.gameid WHERE (start_time >= '2015-11-11' || FIND_IN_SET(opserv_operation.operationid, '17951,17701,17702,17775,17969,17890,17958,17966,17900')) AND completed = 1 AND opserv_operation_attendees.userid = 5750 AND opserv_operation_attendees.status = 4 AND opserv_operation.type <> 5 AND opserv_operation.completed = 1 GROUP BY opserv_operation.gameid
gameid vanguard
16 0
36 1
36 1
36 1
16 0
36 1
27 0
16 0
36 1
36 1
36 1
30 0
36 1
36 1
27 0
36 1
36 1
36 1
So here is the tricky part. I need it to group by and count all the values however if gameid = 30 or vanguard = 1 then they should be all counted together.
So with the above data, the only thing I need is the gameid returned in this case gameid 30. It would have the most counts which would be 13.
There has to be a better way than I have done it.
Edit: So far this one works great thanks to Ed Gibbs. However is still returns two values, I'm guessing only way is to have another subquery? to get just gameid.
SELECT
CASE WHEN vanguard = 1 THEN 30 ELSE gameid END AS gameid,
SUM(count) AS total
FROM (SELECT opserv_operation.gameid, opserv_games.vanguard, COUNT(opserv_games.gameid) AS count FROM opserv_operation_attendees INNER JOIN opserv_operation ON opserv_operation_attendees.operationid = opserv_operation.operationid INNER JOIN opserv_games on opserv_operation.gameid = opserv_games.gameid WHERE (start_time >= '2015-11-11' || FIND_IN_SET(opserv_operation.operationid, '17951,17701,17702,17775,17969,17890,17958,17966,17900')) AND completed = 1 AND opserv_operation_attendees.userid = 5750 AND opserv_operation_attendees.status = 4 AND opserv_operation.type <> 5 AND opserv_operation.completed = 1 GROUP BY opserv_operation.gameid) as l
GROUP BY CASE WHEN vanguard = 1 THEN 30 ELSE gameid END
ORDER BY total DESC
LIMIT 1;
If bottom line you mean "1" and "30" have to be counted together then this should work:
SELECT
CASE WHEN vanguard = 1 THEN 30 ELSE gameid END AS gameid
FROM (... main query ...)
GROUP BY CASE WHEN gameid = 1 THEN 30 ELSE gameid END
ORDER BY SUM(count) DESC
LIMIT 1;

how to select first two rows from each group

Hi We have 3 Table of a music which is something like this in MySql :
1st Table :
the first table is for playlist table where music playlist is exist.
playlistId playlistTitle categoryId
1 hello 0
2 wow 0
3 wi-fi 0
4 awesome 0
5 sixer 1
6 four 1
7 boundary 2
2nd Table :
2nd table is for `songRelation table where every playlist is associated with thier song
playlistId songId
1 4
1 3
1 43
1 57
1 98
2 56
2 67
2 90
2 78
3 98
3 78
3 89
43 90
3rd Table :
the 3rd table is for song table where song detail exist
songId songTitle
4 hello
3 real hero
43 singalone
57 awesom
98 really
78 sakaka
98 shikwa
89 moha
90 hello2
67 Sneh
actually i want to get result something like this :
playlistId songId categoryId songTitle
1 4 0 hello
1 3 0 real hero
2 56 0 singalone
2 67 0 Sneh
3 78 0 sakaka
3 98 0 Shikwa
where the every playlistId will be with their first 2 songId and with their categoryId and also with songTitle.
You can use variables:
SELECT playlistId, songId, categoryId, songTitle
FROM (
SELECT p.playlistId, s.songId, p.categoryId, s.songTitle,
#r := IF (#pid = p.playlistId,
IF (#pid := p.playlistId, #r+1, #r+1),
IF (#pid := p.playlistId, 1, 1)) AS rn
FROM playlist AS p
CROSS JOIN (SELECT #r:=0, #pid:=0) AS vars
INNER JOIN songRelation AS sr ON p.playlistId = sr.playlistId
INNER JOIN song AS s ON sr.songid = s.songid
ORDER BY p.playlistId, s.songId ) AS t
WHERE t.rn <= 2
Variable #r is used to enumerate records within each playlistId slice. Using this in an outer query, we can easily get 2 records per playlistId slice.
Demo here
you can do this using JOIN, try this code:-
SELECT playlist.playlistid, songRelation.songId, song.songTitle
FROM playlist JOIN songRelation JOIN song
WHERE playlist.playlistId=songRelation.playlistId
AND songRelation.songId=song.songId LIMIT 2
you can add the category table and use the same way to get the result table you need, you can also save this query as a virtual table VIEW by writing this code before the code:-
CREATE VIEW myView AS
Edit:-
SELECT playlist.playlistId from playlist INNER JOIN( SELECT playlist.playlistId, songRelation.songId, song.songTitle
FROM playlist JOIN songRelation JOIN song
WHERE playlist.playlistId=songRelation.playlistId
AND songRelation.songId=song.songId
GROUP BY playlist.playlistId LIMIT 2)
WHERE playlist.playlistId=songRelation.playlistId
Please try it, as it is not tested with data, so if you get any issue then create an sqlfiddle so that I can solve the issue.
SELECT x.*
FROM (SELECT pl.playlistid, sng.songId, sng.songTitle,
CASE
WHEN #category != pl.playlistid THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#category := pl.playlistid AS play_list
FROM playlist AS pl
JOIN songRelation AS sr ON sr.playlistid=pl.playlistid
JOIN song AS sng ON sng.songid=sr.songid
JOIN (SELECT #rownum := NULL, #category := '') r
ORDER BY pl.playlistid,sng.songid) X
WHERE x.rank<=2

MySQL complex ranking query

I need to rank users in MySQL where the rank takes into account both ties and continues to counts the tied users as part of the rank.. For example..
points rank
100 1
100 1
100 1
70 4
70 4
60 5
50 6
40 7
40 7
10 8
0 9
0 9
Using the code below I'm ranking as follows...
points rank game
100 1 1
100 1 1
100 1 1
70 2 1
70 2 1
60 3 1
50 4 1
40 5 1
40 5 1
10 6 1
0 7 1
0 7 1
UPDATE rank_table
JOIN (SELECT f.points ,
IF (#lastPoint <> f.points,
#curRank := #curRank +1,
#curRank) AS rank,
#lastPoint := f.points
FROM rank_table f
JOIN (SELECT #curRank := 0, #lastPoint := -1) r
WHERE f.game =1
ORDER BY f.points DESC
) ranks ON (ranks.points = rank_table.points)
SET rank_table.rank = ranks.rank WHERE rank_table.game =1;
Would anyone know it this is possible..?
You do not need any mysql-variable.
Your new rank is the number of players having more points than you.
update
result
join (
select
n.id, count(distinct q.id) total
from result n
left join result q
on
q.points > n.points
group by n.id) m
on
m.id = id
set rank=m.total + 1
(assuming there is some kind of id like player_id)

Return sums of multiples of nth

Now here's a fun MySQL question, I wonder if it's even possible!
Disclaimer: Although it's very similar question that I asked before, it actually is COMPLETELY different. Just saying before anyone says I've asked this before.
For this example lets say I want SUMS() of multiples of 20.
I want to SUM() the row score and return the date.
Lets say I have the following table sorted by date ASC:
Data
score | date
4 2000-01-01
2 2000-01-02
6 2000-01-03
1 2000-01-04 //Score 4+2+6+1 = 13
7 2000-01-05 //Score 4+2+6+1+7 = 20 so return this date
1 2000-01-06
2 2000-01-07
1 2000-01-08
5 2000-01-09
1 2000-01-10
9 2000-01-11 //Score = 39 so far.
7 2000-01-12 //Score = 46 It's not 40 but is the closest number above 40 so return it.
3 2000-01-13
4 2000-01-14
7 2000-01-15 //Score = 60, return this date.
Expected results:
score | date
20 2000-01-05
40 2000-01-12
60 2000-01-15
And etcetera. Is it possible to do this in MySQL?
By using SQL Variables, you don't have to keep doing recursive aggregations for every subsequent row to tally up to the given entity. This does each one in sequence with a flag of which one triggers the multiple of 20. That result is then processed out only where the "ThisOne" flag is set to 1.
select
M20.*
from
( select
TransDate,
score,
if( #runTotal + Score >= 20 * #multCnt, 1, 0 ) as ThisOne,
#multCnt := #multCnt + if( #runTotal + Score >= 20 * #multCnt, 1, 0 ) as nextSeq,
#runTotal := #runTotal + Score
from Mult20s,
( select #multCnt := 1,
#runTotal := 0 ) sqlvars
order by transdate ) M20
where
M20.ThisOne = 1
Sure, anything's possible :)
select
floor(partial / 20) * 20, min(date)
from
(select
(select sum(score) from Scores s2
where s2.date <= s.date) as partial,
score,
date
from
Scores s) p
where
floor(partial / 20) > 0
group by
floor(partial / 20)
Demo: http://www.sqlfiddle.com/#!2/d44cf/3