Create a Cumulative Sum Column in MySQL Based On an ID - mysql

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 ;

Related

MySQL Leaderboard Table

I'm trying to figure out how to Select a specific number of rows from a MySQL table based on WHERE clause. I have a table with 10 dummy users, I want to get 2 previous and 2 next users of specific user with their ranks.
user_id | points
==================
10 200
4 130
2 540
13 230
15 900
11 300
3 600
17 110
20 140
1 430
5 800
I achieved adding a column for ranking like:
user_id | points | rank
===========================
15 900 1
5 800 2
3 600 3
2 540 4
1 430 5
11 300 6
13 230 7
10 200 8
20 140 9
4 130 10
17 110 11
But the problem is that I want only 5 rows. Suppose I'm retrieving data for user with user_id = 11. The output should look like this:
user_id | points | rank
===========================
2 540 4
1 430 5
11 300 6
13 230 7
10 200 8
where user_id = 11 is in the centre with 2 rows above and 2 below. I have tried nesting UNIONS and SELECT statements but nothing seems to work properly.
Here's a suggestion if you're on MySQL 8+:
WITH cte AS (
SELECT user_id, points,
ROW_NUMBER() OVER (ORDER BY points DESC) AS Rnk
FROM mytable)
SELECT cte2.user_id,
cte2.points,
cte2.Rnk
FROM cte cte1
JOIN cte cte2
ON cte1.user_id=11
AND cte2.Rnk >= cte1.Rnk-2
AND cte2.Rnk <= cte1.Rnk+2
Using common table expression (cte) then do a self join with condition of user_id=11 as base to get the Rnk value of -2 and +2.
Demo fiddle
Since you're on older MySQL version, here's what I can suggest:
SET #uid := 11;
SET #Rnk := (SELECT Rnk
FROM
(SELECT user_id, points,
#r := #r+1 AS Rnk
FROM mytable
CROSS JOIN (SELECT #r := 0) r
ORDER BY points DESC) v
WHERE user_id = #uid);
SELECT user_id, points, Rnk
FROM
(SELECT user_id, points,
#r := #r+1 AS Rnk
FROM mytable
CROSS JOIN (SELECT #r := 0) r
ORDER BY points DESC) v
WHERE Rnk >= #Rnk-2
AND Rnk <= #Rnk+2;
If you will only use user_id as base, then the only part here you need to change is the SET #uid. The remaining queries are just fulfilling your condition of getting two positions above and below the rank retrieved according to the user_id. The base query in SET #Rnk is the same as the base query for the last one. The idea is to assign #Rnk variable with Rnk position of user_id=11 then use it in WHERE condition for the last query.
I'm not aware if there's any online fiddle still using MySQL 5.1 but here's probably the closest version to it, MySQL 5.5 demo fiddle.

Aggregate fields selectively based on MAX(value)

I have a very large union query that produces the following results:
p_id title_id title is_live
1 37 TITANIC 1
2 37 TITANIC 0
3 41 AVATAR 0
4 41 AVATAR 0
5 44 HOME ALONE 0
6 11 COMPUTE 1
7 11 COMPUTE 1
8 11 COMPUTE 1
From this result, I want to aggregate it so that I get at least one of each title (title_id), but all titles with is_live = True. (In other words, for each title_id, I want to get exactly one result if MAX(is_live)=0, but all results if MAX(is_live)=1 for that title_id.) Here's an example of the result set that I want, with an explanation for each title:
p_id title_id title is_live
1 37 TITANIC 1 // live one for titanic
3 41 AVATAR 0 # at least one of each title (which one doesn't matter)
5 44 HOME ALONE 0 /* at least one of each title */
6 11 COMPUTE 1 // keep all, since all are live
7 11 COMPUTE 1
8 11 COMPUTE 1
The query that I have so far is along the lines of the following:
SELECT * FROM (
80-line UNION query
) d
GROUP BY
d.title_id
But this of course isn't accurate enough and is more an outline. How would I do the above?
Here is a SQLFiddle for the question: http://sqlfiddle.com/#!9/739a36/2/0
You can do this with variables:
select q.*
from (select q.*,
(#rn := if(#t = title, #rn + 1,
if(#t := title, 1, 1)
)
) as rn
from (<your query here>) q cross join
(select #t := '', #rn := 0) params
order by title, is_live desc
) q
where is_live = 1 or rn = 1;
Here is the SQL Fiddle.
One way to do it using union all, first selecting all is_live true rows and selecting one false is_live row for the title_id's not previously selected.
select *
from `a`
where is_live
union all
select max(p_id),title_id,max(title),max(is_live)
from `a` a1
where not is_live
and not exists (select 1 from `a` a2
where a1.title_id=a2.title_id and a2.is_live)
group by title_id

Mysql Query Min And Max with Limit N Grouping

I'd like to ask some question about query.
This is my case:
Structure Table
codenumber varchar (PK)
prize varchar
batchno double
category varchar
Sample Data On Database:
Code Prize BatchNumber Category
1000000231 TRY AGAIN 1 A
1000000238 TRY AGAIN 2 A
1000000376 TRY AGAIN 3 A
1000000473 TRY AGAIN 4 A
1000000934 50 5 A
1000001281 50 6 B
1000001894 50 7 B
1000002014 TRY AGAIN 8 B
1000002831 TRY AGAIN 9 B
1000003123 TRY AGAIN 10 B
1000003158 TRY AGAIN 11 C
1000003224 TRY AGAIN 12 C
1000003524 TRY AGAIN 13 C
1000003598 50 14 C
1000003616 TRY AGAIN 15 C
1000003657 TRY AGAIN 16 A
1000003959 50 17 A
1000004289 TRY AGAIN 18 A
1000004529 TRY AGAIN 19 A
1000004853 TRY AGAIN 20 A
1000005683 TRY AGAIN 21 B
1000005728 100 22 B
1000005816 TRY AGAIN 23 B
1000006325 TRY AGAIN 24 B
I wanted to get the Minimum and Maximum batch number for each 5 rows.
Then how to get the query result like below:
Category MinBatch MaxBatch
A 1 5
B 6 10
C 11 15
A 16 20
B 21 24
Please Help Thanks
Presuming that batch represents the ordering for determining groups of 5, you can do this with variables:
select category, min(batch), max(batch)
from (select s.*, (#rn := #rn + 1) as rn
from structure s cross join
(select #rn := 0) params
order by batch
) s
group by floor((rn - 1) / 5)
order by min(batch);
Actually, if you know the batches are consecutive with no gaps and start at 1:
select category, min(batch), max(batch)
from structure s
group by floor((batch - 1) / 5)
order by min(batch);
Below query will give you the result
select category, min(batchnumber)as 'MinBatch', max(batchnumber)as 'MaxBatch'
from tablename order group by (category)

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