MySQL calculation of cumulative sum with a reset condition - mysql

Is there a way to capture a cummulative sum with a reset condition in mySQL? The use case for this is a simple cash register table
id transactionType value CurrentBalance
1 purchase 10 10
2 sale -10 0
3 RESET 20 20
4 purchase 10 30
5 sale -10 20
6 sale 10 30
7 sale -20 10
The answer from here is a good starting point but I don't see how to extend it:
Create a Cumulative Sum Column in MySQL
SELECT t.id,
t.count,
#running_total := #running_total + t.value AS CurrentBalance
FROM TABLE t
JOIN (SELECT #running_total := 0) r
ORDER BY t.id
NOTE: basically the idea is to reset the cumulative sum each time a reset is hit. Ideally I am trying to have an on update/insert trigger which will update the entire CurrentBalance column taking into account RESETs. This is a small table so I don't mind updating the whole table in exchange for something simple.

All this requires is some simple conditional logic:
SELECT t.id, t.count,
#running_total := if(transactionType = 'RESET', t.value,
#running_total + t.value
) as CurrentBalance
FROM TABLE t JOIN
(SELECT #running_total := 0) params
ORDER BY t.id;

Related

Cumulative sum query on foreign key

I want to write a query for cumulative sum in MYSQL. I have a foreign key in my table and I want to add their hours as a cumulative sum.
Table 1
id(not primary key) Hours
1 4
2 4
1 5
I have tried this query
select spent_hours := spent_hours + hours as spent
from time
join (select spent_hours := 0) s
I am getting this
id(not primary key) hours spent
1 4 4
2 4 8
1 5 13
But I want this result:
id(not primary key) Hours spent
1 4 4
2 4 4
1 5 9
Since you have an autoincrement field (let's assume for this case its called record_id) you can use this little trick to achieve what you want:
SELECT Main.id, Main.spentHours,
(
SELECT SUM(spentHours)
FROM Table1 WHERE Table1.id = Main.id
AND Table1.record_id >= Main.record_id
) as totalSpentHours
FROM Table1 Main
ORDER BY Main.record_id ASC
This will fetch the id, current spent hours, along using a subselect, all hours from the current ID and above for that user.
You need additional an variable to keep track of the cumulative sum within each id:
select t.id, t.hours,
(#h := if(#i = id, #h + spent_hours,
if(#i := id, spent_hours, spent_hours)
)
) as spent
from time cross join
(select #h := 0, #i := 0) params
order by id, ??;
Note: you need an additional column to specify the order for the cumulative sum (indicated by ?? in the order by clause. Remember that SQL tables represent unordered sets, so you need a column to explicitly represent ordering.

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;

query to sum a column until get defined value

i've tried some other topics for this but couldn't get answers that meet my requirement so posting a new question. sorry bout this.
i'm trying to query on mysql to get a 'sum' data until it reaches the defined value. like
from my table 'purchase', for each 'sid' starting from the last row, i need sum of 'pqty' until the result equals a value from string (but to try i've given a certain value).
let me define with the rows from my table---
the rows for 'sid=1' from 'purchase' are like this---
date pqty prate pamt
2014/04/29 5 38000 190000
2014/05/04 1 38000 38000
2014/05/13 20 35000 700000
2014/05/19 1 38000 38000
from this row, starting from the last row i want to 'sum(pqty) until it reaches 19(for now). it is achieved from adding last 2 rows(for 19). and stop sum here and return valus or sum of 'pqty', 'prate' and 'pamt'. to achieve this i tried the following according to example found on this forum.
SELECT date, pqty, #total := #total + pqty AS total
FROM (purchase, (select #total :=0) t)
WHERE #total<19 AND sid = $sid ORDER BY date DESC
but it's not working for me. please guide me through this. also suggest something else if this is not the good technique for my purpose.
thankz in advance.....
Not 100% certain, but I think both of these work...
SELECT x.*, SUM(y.pqty) FROM purchase x
JOIN purchase y
ON y.date >= x.date
GROUP
BY x.date
HAVING 19 BETWEEN SUM(y.pqty)-x.pqty AND SUM(y.pqty)
OR 19 >= SUM(y.pqty);
SELECT a.*
FROM
( SELECT x.*, #i := #i+pqty i
FROM purchase x
, (SELECT #i:= 0) var
ORDER
BY x.date DESC
) a
WHERE 19 BETWEEN a.i-a.pqty AND a.i
OR 19 >= a.i;

Mysql query to calculate total cost

HI
I have a table listsing_prices (id,listing_id,day_from,day_to,price)
I need to calculate the total cost of an holiday in mysql becouse I need to sort the results by total cost.
EX:
VALUES IN TABLE
1 6 2011-04-27 2011-04-30 55,00
2 6 2011-05-01 2011-05-02 60,00
3 6 2011-05-03 2011-05-15 65,00
holiday from 2011-04-28 to 2011-05-05 total cost = 480
Without creating an actual table to represent every day from start date to end date, you could use mysql query variables. The first query can join to any table as long as it has as many records as days you are concerned with for the hoiday period... in this case, 8 days from April 28 to May 5. By doing a Cartesian and limiting to 8 will in essence, create a temp result set with one record per each day, starting with 2011/04/28 (your starting date).
Then, this is joined back to your pricing table that matches the date period and sums the matching price for total costs...
select
sum( pt.price ) as TotalCosts
from
( SELECT
#r:= date_add(#r, interval 1 day ) CalendarDate
FROM
(select #r := STR_TO_DATE('2011/04/28', '%Y/%m/%d')) vars,
AnyTableWithAtLeast8ays limit 8 ) JustDates,
PricesTable pt
where
JustDates.CalendarDate between pt.date_from and pt.date_to
select count(price) from listing_prices where day_from >= '2011-04-28' and day_to <= '2011-05-05'
-- This will provide a list of ids along with how many days fall between the two
SELECT a.id, DATEDIFF(DAYS, CASE WHEN day_from < '2011-04-28' THEN '2011-04-28' ELSE day_from END CASE, day_to) AS DayCount
FROM listing_prices a
WHERE '2011-04-28' BETWEEN a.date_from AND a.date_to
AND a.date_to <= '2011-05-05'
-- Based on the previous query, sum the number of days within the range
SELECT SUM( a.price * b.DayCount ) AS Total
FROM listing_prices a
JOIN ( SELECT a.id, DATEDIFF(DAYS, CASE WHEN day_from < '2011-04-28' THEN '2011-04-28' ELSE day_from END CASE, day_to) AS DayCount
FROM listing_prices a
WHERE '2011-04-28' BETWEEN a.date_from AND a.date_to
AND a.date_to <= '2011-05-05'
) b ON a.id = b.id
Please note that this is untested ... the query at the top I believe should work but if it doesn't, it can be modified and so that it does work (get the number of days within each range) and then literally copied and pasted into the subquery of the second query. The second query is the one that you will actually use.

making daily report with MySQL

i want to make some daily report which order by date.
i want this data can increase every day.
Date qty QP
2010-09-01 10 10
2010-09-02 3 13 (it means result QP from 2010-09-01 accumulate with 2010-09-02)
2010-09-03 8 21
this is the 1st code:
SELECT Date, SUM(number) AS qty FROM calc GROUP BY Date
how do i do to show "QP" if for actually i dont need to show "qty" field(automatic count) just show Date and QP but it still can count?
SET #r := 0;
SELECT date, #sum := SUM(number) AS qty, #r := #r + #sum AS qp
FROM calc
GROUP BY
date
This example will help you for sure:
MySQL select "accumulated" column