Return sums of multiples of nth - mysql

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

Related

I am doing an election system, I am doing a graphic with ChartJS, so I need to COUNT votes GROUP BY date, then sum it with previous day

|voters_id| created_at |
6 20/07/2022
7 21/07/2022
9 21/07/2022
14 22/07/2022
21 22/07/2022
The output that I want is:
----------------------------------------- > (old + today)
on date 20/07 i must have votation count= (0 +1) = 1
on date 21/07 i must have votation count= (1 +2) = 3
on date 22/07 i must have votation count= (3 +2) = 5
How should I write this query?
You can use count() with windowing function. ie:
select distinct created_at, count(*) over (order by created_at) as votes
from mytable;
DBFiddle demo

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 best students in every class in a school

In MySql I need to select top student in every class in a school in termid=10 to get discount for next term enrollment .
Please notice that total is not in table(I put in below for clearing problem)
I have this workbook table for all students workbook:
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
2 1 22 40 20 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
5 7 22 10 40 50 10
6 8 11 10 30 40 10
7 9 33 20 45 65 10
8 11 11 null null null 10
9 12 54 null null null 02
10 13 58 null null null 02
1st challenge is : exam1 and exam2 are VARCHAR and total is not in table (as i explained).
2nd challenge is : as you can see in id=8 std #11 has not numbers
3rd challenge is : may be two students have top level so they must be in result.
I need result as :
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
2 1 22 40 20 60 10
i have this query but not work good as i mention.
SELECT DISTINCT id,studentid,classid,exam1,exam2,total,termid ,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) FROM workbook WHERE ClassId = '10';
You can get the total for the students by just adding the values (MySQL will convert the values to numbers). The following gets the max total for each class:
select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid;
You can then join this back to the original data to get information about the best students:
select w.*, coalesce(w.exam1, 0) + coalesce(w.exam2, 0) as total
from workbook w join
(select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid
) ww
on w.classid = ww.classid and (coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) = ww.maxtotal;
Another approach is to join the table with itself. You find out the max for each class and then join all students of this class which match the class max:
max for each class (included in the final statement already):
SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid
The complete statement:
SELECT s2.*, s1.maxtotal
FROM (SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid) s1
JOIN students s2 ON s1.classid = s2.classid
WHERE s1.maxtotal = (CAST(s2.exam1 AS UNSIGNED) + CAST(s2.exam2 AS UNSIGNED));
SQL Fiddle: http://sqlfiddle.com/#!2/9f117/1
Use a simple Group by Statement:
SELECT
studentid,
classid,
max(coalesce(exam1,0)) as max_exam_1,
max(coalesce(exam2,0)) as max_exam_2,
sum(coalesce(exam1,0) + coalesce(exam2,0)) as sum_exam_total,
termid
FROM
workbook
WHERE
termid=10
GROUP BY
1,2
ORDER BY
5
Try something like this:
SELECT id,studentid,classid,exam1,exam2,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) AS total,termid FROM `workbook` WHERE ((CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2)))) > 50
Thanks all my friends
I think combine between 2 answer in above is best :
SELECT s2.*, s1.maxtotal
FROM (SELECT ClassId, MAX(
coalesce(exam1,0)+
coalesce(exam2,0)
) as 'maxtotal'
FROM workbook
WHERE
(
termid = '11'
)
GROUP BY ClassId) s1
JOIN workbook s2 ON s1.ClassId = s2.ClassId
WHERE s1.maxtotal = (
coalesce(exam1,0)+
coalesce(exam2,0)
) AND (s1.maxtotal >'75');
last line is good for s1.maxtotal=0 (some times student scores have not be entered and all equals 0 so all will shown as best students) or some times we need minimum score (to enroll in next term).
So thanks all

SQL Query to get number of times and duration of a column values reaches a specific value in a sequence

I have a table with two columns ReceivedOn(Date/Time) and Speed(int)
Sample data will look like below
ReceivedOn | Speed
----------------------------------------------
2012-11-05 06:30:00 10
2012-11-05 06:31:00 45
2012-11-05 06:32:00 48
2012-11-05 06:33:00 53
2012-11-05 06:34:00 47
2012-11-05 06:35:00 38
2012-11-05 06:36:00 22
2012-11-05 06:37:00 36
2012-11-05 06:38:00 41
2012-11-05 06:39:00 47
2012-11-05 06:40:00 49
2012-11-05 06:41:00 22
2012-11-05 06:42:00 36
I need to group the rows when speed > 40, so that the resulting output would be
StartTime | EndTime | Count
--------------------------------------------------------
2012-11-05 06:31:00 2012-11-05 06:34:00 4
2012-11-05 06:38:00 2012-11-05 06:40:00 3
StartTime to be the ReceivedOn value when the speed first crossed 40, and the EndTime value to be the ReceivedOn value when it was last over 40 in consecutive records, with the Count being the total number of consecutive records that were over 40.
I tried my best but unable to get it. Is it possible to get this with sql query?
Please suggest. Thanks in advance.
Here's a general answer to questions like these.
Detect consecutive items meeting particular criteria in a time series
Your specific case seems to be easier because you have no time gaps (said he hopefully). You are trying to find the gaps in your time sequence. In your case the gaps are defined as those items that are >= 40. So, you're looking for gaps in the sequence of events with values less than forty.
Here's a query that gives your time squence with row numbers.
SELECT #RowA := #RowA + 1 AS ROWNUM,
ReceivedOn, Speed
FROM (
SELECT ReceivedOn, Speed
FROM obs
WHERE NOT Speed >= 40
) AS A
JOIN (SELECT #RowA := 0) AS B
Now you use a some serious SQL monkey business to self-join this sequence to itself. That works like this:
SELECT B.ReceivedON + INTERVAL 1 MINUTE As StartTime,
A.ReceivedOn - INTERVAL 1 MINUTE AS EndTime,
-1 + TIMESTAMPDIFF(MINUTE, B.ReceivedOn, A.ReceivedOn) AS Count
FROM (
SELECT #RowA := #RowA + 1 AS ROWNUM,
ReceivedOn, Speed
FROM (
SELECT ReceivedOn, Speed
FROM obs
WHERE NOT Speed >= 40
) AS A
JOIN (SELECT #RowA := 0) AS B
) AS A
JOIN (
SELECT #RowB := #RowB + 1 AS ROWNUM,
ReceivedOn, Speed
FROM (
SELECT ReceivedOn, Speed
FROM obs
WHERE NOT Speed >= 40
) AS A
JOIN (SELECT #RowB := 0) AS B
) AS B ON B.ROWNUM+1 = A.ROWNUM
WHERE TIMESTAMPDIFF(MINUTE, B.ReceivedOn, A.ReceivedOn) > 1
http://sqlfiddle.com/#!2/2cb57/24/0
This looks really hairy, but it is simply a join of that first query to itself ON B.ROWNUM+1 = A.ROWNUM. That lines up that query's result set to itself offset by one row, so you can compare consecutive rows.
That gives the result you need. Notice that if your first observation is >= 40, this query will leave out the first sequence of observations.

MySQL query that gets all rows UNTIL the SUM(column) is bigger than X

I have the following data
user_id days date
88 2 2013-08-25
88 4 2013-08-23
88 18 2013-08-5
88 1 2013-08-4
88 2 2013-08-2
73 11 2013-08-2
299 4 2013-08-2
12 983 2013-08-2
I'm trying to get all recent rows (order by DATE desc) for a specific user_id , until the SUM of days column is bigger than X. For example in this case if X=7 I would get the three first rows with SUM(days)=24.
Try this. Here you will use a local variable that will count the sums in the subquery.
select
user_id,
days,
date
from
(
select
user_id,
days,
date,
#sum_days := #sum_days + days as sum_days
from
myTable
order by
date desc
) t
cross join (select #sum_days := 0) const -- resetting your #sum_days var.
where
sum_days < X -- fill a number in for X here.