How to calculate the remaining amount per row? - mysql

I have a wallet table like this:
// wallet
+----+----------+--------+
| id | user_id | amount |
+----+----------+--------+
| 1 | 5 | 1000 |
| 2 | 5 | -200 |
| 3 | 5 | -100 |
| 4 | 5 | 500 |
+----+----------+--------+
I want to make a view that calculates the remaining amount per row. Something like this:
+----+----------+--------+------------------+
| id | user_id | amount | remaining_amount |
+----+----------+--------+------------------+
| 1 | 5 | 1000 | 1000 |
| 2 | 5 | -200 | 800 |
| 3 | 5 | -100 | 700 |
| 4 | 5 | 500 | 1200 |
+----+----------+--------+------------------+
Any idea how can I do that?

MySQL 8 has window function for that purpose, like SUM() OVER
for your sample data, this will calculate the running SUM for every user_id
vital for th function to work is the PARTITION BY and the ORDER BY to get the right amount
The PARTITION BY is used to get sums for a user_id, so if you had user 5,6,7,8 it will correctly add (or subtract) the maount theat that user produced.
The ORDER BYis needed to get the right mount at the corect position. Tables are by nature unsortede, so an ORDER BY is needed to give the outout the corect order, if the ids where changed, you would get another mount, as it will be prior added to the running sum
SELECT
`id`, `user_id`, `amount`
, SUM(`amount`) OVER(PARTITION BY `user_id` ORDER BY `id`) run_sum
FROM wallet
id
user_id
amount
run_sum
1
5
1000
1000
2
5
-200
800
3
5
-100
700
4
5
500
1200
fiddle

Do not know if this meets your demands or not
SELECT
t1.id,t1.user_id,t1.amount,
(
SELECT sum(t2.amount) FROM yourtable t2 WHERE t2.id<=t1.id AND t1.user_id=t2.user_id
) as remaning_amount
FROM yourtable t1

Related

Latest datetime from unique mysql index

I have a table. It has a pk of id and an index of [service, check, datetime].
id service check datetime score
---|-------|-------|----------|-----
1 | 1 | 4 |4/03/2009 | 399
2 | 2 | 4 |4/03/2009 | 522
3 | 1 | 5 |4/03/2009 | 244
4 | 2 | 5 |4/03/2009 | 555
5 | 1 | 4 |4/04/2009 | 111
6 | 2 | 4 |4/04/2009 | 322
7 | 1 | 5 |4/05/2009 | 455
8 | 2 | 5 |4/05/2009 | 675
Given a service 2 I need to select the rows for each unique check where it has the max date. So my result would look like this table.
id service check datetime score
---|-------|-------|----------|-----
6 | 2 | 4 |4/04/2009 | 322
8 | 2 | 5 |4/05/2009 | 675
Is there a short query for this? The best I have is this, but it returns too many checks. I just need the unique checks at it's latest datetime.
SELECT * FROM table where service=?;
First you need find out the biggest date for each check
SELECT `check`, MAX(`datetime`)
FROM YourTable
WHERE `service` = 2
GROUP BY `check`
Then join back to get the rest of the data.
SELECT Y.*
FROM YourTable Y
JOIN ( SELECT `check`, MAX(`datetime`) as m_date
FROM YourTable
WHERE `service` = 2
GROUP BY check) as `filter`
ON Y.`service` = `filter`.service
AND Y.`datetime` = `fiter`.m_date
WHERE Y.`service` = 2

mysql Quiz leaderboard filter by points, time taken

I have a quiz report table which shows a report for every quiz a user takes. I need to create a leaderboard from this, which shows the top users best score, filtering by points and then time taken.
here is a link to a sql fiddle http://sqlfiddle.com/#!2/65fbf0/1
I am really struggling as i need to filter the results by two columns for one user, my ideal result would be
Results for Quiz id 1
---------------------------------------------------------------
| user_id | points | time_spend | start_dt | quiz_id |
| 1 | 3 | 0.5 | May,15 2015| 1 |
| 2 | 3 | 0.8 | May,15 2015| 1 |
| 3 | 2 | 0.5 | May,15 2015| 1 |
Then a separate query for all quiz's showing the results from the last week
Results from all Quizzs
---------------------------------------------------------------
| user_id | points | time_spend | start_dt | quiz_id |
| 1 | 3 | 0.5 | May,15 2015| 1 |
| 2 | 3 | 0.8 | May,13 2015| 3 |
| 3 | 2 | 0.5 | May,12 2015| 2 |
You can sort on multiple columns like this:
select *
from QuizReport
where quiz_id = 1
order by points desc, time_spend asc;
select *
from (
select *
from QuizReport
where start_dt >= subdate(curdate(), 7)
order by points desc, time_spend asc) a
group by user_id;
group_by user_id preserves the first row for every user_id. since the inner query sorts rows by score, the outer query will display best row for every user.

How do I self-join the result of an aggregate function in MySQL?

I have some data in the following format:
Table: MountHeights
ID | Height| Type | ItemCode | Context
--------------------------------------------
1 | 15 | max | BD1896-1W | exterior
2 | 12 | max | BD1896-1W | insect
3 | 18 | max | BD1896-1W | interior
4 | 13 | max | BD14120-1W | exterior
5 | 10 | max | BD14120-1W | insect
6 | 15 | max | BD14120-1W | interior
There are multiple rows for each of the item codes.
I'm trying to figure out a way to get max(Height) for each ItemCode where the Type='max', for some reason, I can't quite wrap my head around how I should reference the table to itself.
I'd like the result to be along the lines of:
Results:
max(Height)| ItemCode
---------------------------
18 | BD1896-1W
15 | BD14120-1W
How do I do that?
There is no need to self-reference the table here.
SELECT `ItemCode`, MAX( `height` )
FROM `MountHeights`
WHERE `Type`= 'max'
GROUP BY `ItemCode`
Example Fiddle
You can also do:
SELECT t1.ItemCode,max(t1.height) as height
from MountHeights as t1, test as t2
where t1.Type=t2.Type group by t1.Type

MySQL - Get row with the maximum HISTORY ID for COMPONENT IDs in non-existing months

I have a table INVENTORY which consists of inventory items. I have the following table structure:
INSTALLATION_ID
COMPONENT_ID
HISTORY_ID
ON_STOCK
LAST_CHANGE
I need to obtain the row with the max HISTORY ID for records for which the spcified LAST_CHANGE month doesn't exist.
Each COMPONENT_ID and INSTALLATION_ID can occur multiple times, they are distinguished by their respective HISTORY_ID
Example:
I have the following records
COMPONENT_ID | INSTALLATION_ID | HISTORY_ID | LAST_CHANGE
1 | 100 | 1 | 2013-01-02
1 | 100 | 2 | 2013-02-01
1 | 100 | 3 | 2013-04-09
2 | 100 | 1 | 2013-02-22
2 | 100 | 2 | 2013-03-12
2 | 100 | 3 | 2013-07-07
2 | 100 | 4 | 2013-08-11
2 | 100 | 5 | 2013-09-15
2 | 100 | 6 | 2013-09-29
3 | 100 | 1 | 2013-02-14
3 | 100 | 2 | 2013-09-23
4 | 100 | 1 | 2013-04-17
I am now trying to retrieve the rows with the max HISTORY ID for each component but ONLY for COMPONENT_IDs in which the specifiec month does not exists
I have tried the following:
SELECT
INVENTORY.COMPONENT_ID,
INVENTORY.HISTORY_ID
FROM INVENTORY
WHERE INVENTORY.HISTORY_ID = (SELECT
MAX(t2.HISTORY_ID)
FROM INVENTORY t2
WHERE NOT EXISTS
(
SELECT *
FROM INVENTORY t3
WHERE MONTH(t3.LAST_CHANGE) = 9
AND YEAR(t3.LAST_CHANGE)= 2013
AND t3.HISTORY_ID = t2.HISTORY_ID
)
)
AND INVENTORY.INSTALLATION_ID = 200
AND YEAR(INVENTORY.LAST_CHANGE) = 2013
The query seems to have correct syntax but it times out.
In this particular case, i would like to retrieve the maximum HISTORY_ID for all components except for those that have records in September.
Because I need to completely exclude rows by their month, i cannot use NOT IN, since they will just suppress the records for september but the same component could show up with another month.
Could anybody give some pointers? Thanks a lot.
If I understand correctly what you want you can do it like this
SELECT component_id, MAX(history_id) history_id
FROM inventory
WHERE last_change BETWEEN '2013-01-01' AND '2013-12-31'
AND installation_id = 100
GROUP BY component_id
HAVING MAX(MONTH(last_change) = 9) = 0
Output:
| COMPONENT_ID | HISTORY_ID |
|--------------|------------|
| 1 | 3 |
| 4 | 1 |
If you always filter by installation_id and a year of last_change make sure that you have a compound index on (installation_id, last_change)
ALTER TABLE inventory ADD INDEX (installation_id, last_change);
Here is SQLFiddle demo

MySQL using JOIN and reports

So, I've got a mysql query that looks like this:
SELECT rewards.name, redemptions.points, redemptions.value
FROM rewards
INNER JOIN redemptions ON rewards.id = redemptions.reward_id;
The problem is that it spits out a table like this:
-----------------------------
| Reward 1 | 500 | 30 |
-----------------------------
| Reward 1 | 500 | 30 |
-----------------------------
| Reward 1 | 500 | 30 |
-----------------------------
| Reward 2 | 100 | 10 |
-----------------------------
| Reward 2 | 100 | 10 |
-----------------------------
| Reward 3 | 250 | 20 |
-----------------------------
and so on. Ideally, what I would actually like it to do is to only list each one once, but sum certain columns. So for example, it would look something like this:
-----------------------------
| Reward 1 | 500 | 90 |
-----------------------------
| Reward 2 | 100 | 20 |
-----------------------------
| Reward 3 | 250 | 20 |
-----------------------------
Where it is summing the third column, but listing the first two columns just once. I thought maybe a union would do it because I know it ignores duplicates, but I don't think it works while using unions as well.
Use SUM, and remember to add GROUP BY:
SELECT rewards.name, redemptions.points, SUM(redemptions.value)
FROM rewards
INNER JOIN redemptions ON rewards.id = redemptions.reward_id
GROUP BY rewards.name;
SELECT a.name, b.points, SUM(b.value)
FROM rewards a
INNER JOIN redemptions b
ON a.id = b.reward_id
GROUP BY a.name, b.points