having this table
#table stock
+-------+----------+
| id | stock |
+-------+----------+
| 1 | 20 |
+-------+----------+
| 2 | 25 |
+-------+----------+
| 3 | 10 |
+-------+----------+
| 4 | 20 |
+-------+----------+
#note: this is an arbitrary random data
How can I keep selecting rows from the table till the sum() of the stock column reaches some value or a little higher , and the table is ORDER BY id ASC.
For example I want to select rows from the table till I have sum of stock '50' , so the result will be
#result 3 rows
+-------+----------+
| id | stock |
+-------+----------+
| 1 | 20 |
+-------+----------+
| 2 | 25 |
+-------+----------+
| 3 | 10 |
+-------+----------+
the sum of stock now is '55' which is the closest possible higher value than '50' , and if we take the next row id:4 the sum of stock will be higher than 50 , and if we remove the row id:3 the value will be 45 which is less than the stock I want 50 .
I can achieve this with PHP by selecting all the rows and loop throw them, but I guess that will be a waste. Is there a possible way to do that on a lower level and let mysql do that for me by a sql query?
Thank you and forgive me if I messed something , I'm new to programming
You need a cumulative sum for this to work. One method uses variables:
select t.*
from (select t.*, (#sum := #sum + stock) as cume_stock
from t cross join
(select #sum := 0) params
order by id
) t
where cume_stock < 50 or (cume_stock >= 50 and cume_stock - stock < 50);
Related
I'm trying to create a query that creates a column that adds all the row value of the previous column per row. I've tried SUM and COUNT but this is not giving me the result I want. How should I tackle this problem?
+----+------+-----+
| id |amount|total|
+----+------+-----+
| 1 | 10 | 10 |
| 2 | 20 | 30 |
| 3 | 15 | 45 |
| 4 | 30 | 75 |
+----+------+-----+
It is a Rolling Sum problem. In MySQL 8.0.2 and above, you can solve this using Window functions with Frames. In older versions, we can do the same using User-defined Session variables.
Try:
SELECT
dt.id,
dt.amount,
#tot := #tot + dt.amount AS total
FROM
(
SELECT
id,
amount
FROM your_table_name
ORDER BY id
) AS dt
CROSS JOIN (SELECT #tot := 0) AS user_init
This question already has answers here:
Selecting the Max and Min records in one MySQL command
(5 answers)
Closed 4 years ago.
How can I select and display value of that field in a row of a table whose value is maximum or minimum ?
For example, say I have the following table structure for balance.
+------+-------+--------+
| id | bal | userid |
+------+-------+--------+
| 1 | 4.00 | 1 |
| 2 | 8.35 | 2 |
| 3 | 15.67 | 3 |
| 4 | 10.00 | 4 |
+------+-------+--------+
Here, I want to show the users with maximum balance and minimum balance. What should be my query to pick show them? Like here in this case user 3 has maximum balance and user 1 has minimum. I want to pick them and LEFT JOIN them with my members table using mem_id to extract their username mem_uname and show their balance.
One option would be the following union query:
(SELECT *, 'max user' AS type FROM balance ORDER BY bal DESC LIMIT 1)
UNION ALL
(SELECT *, 'min user' FROM balance ORDER BY bal LIMIT 1)
This assumes that you don't need to worry about ties for greatest/least balance. If this be a concern, then we would have to do more work:
SELECT *, 'max users' AS type
FROM balance
WHERE bal = (SELECT MAX(bal) FROM balance)
UNION ALL
SELECT *, 'min users'
FROM balance
WHERE bal = (SELECT MIN(bal) FROM balance)
ORDER BY type;
try the following query
(select * from balance where bal=(select max(bal) from balance))
union all
(select * from balance where bal=(select min(bal) from balance));
OUT PUT
+------+-------+--------+
| id | bal | userid |
+------+-------+--------+
| 3 | 15.67 | 3 |
| 1 | 4 | 1 |
+------+-------+--------+
I have a SQL database I am trying to access from, what I require is the average for all terms given a difference between the max and min of each entry being above a certain threshold.
So, for interests sake say we have the following:
+------------+------+---------+
| Date | Name | Number |
+------------+------+---------+
| 2017-01-03 | Dude | 1000000 |
| 2017-01-03 | Dude | 2000000 |
| 2017-01-04 | Dude | 7000000 |
| 2017-01-04 | Dude | 8750000 |
+------------+------+---------+
I now want to take the averages for date 2017-01-03, but only if the difference between the max and min number for that day is above/below X. Of course my actual table is much larger, so removing the data and looping in VBA, for instance, is not helpful.
My ideal output would be:
+------------+------+---------+
| Date | Name | AVGnum |
+------------+------+---------+
| 2017-01-03 | Dude | X |
| 2017-01-04 | Dude | Y |
+------------+------+---------+
Where X and Y are the averages of the numbers if and only if the difference between the max and min on that day is above a specified X.
Thanks a lot!!!!
Something like this:
select date, name,
(case when max(num) - min(num) > #X then avg(num) end) as avgnum
from t
group by date, name;
This puts NULLs in the places where the difference does not meet the condition.
If you want to filter out the rows, use having instead:
select date, name, avg(num) as avgnum
from t
group by date, name
having max(num) - min(num) > #X
somthing like:
SELECT table.Date,
table.Name,
AVG(table.number)
FROM table
WHERE table.Date = '2017-01-03'
GROUP BY table.Date,
table.Name
HAVING MAX(table.Number) - MIN(table.Number) < X
AND MAX(table.Number) - MIN(table.Number) > X
Be aware that only the numbers matching the having conditions would be taken in the AVG calculation.
With 'HAVING' you can use aggregate functions in GROUP BY.
Assuming I have something like this :
MySQL table
Date | Val
2013/11/22 | 2
2013/11/23 | 4
2013/11/25 | 12
2013/11/30 | 28
2013/12/02 | 2
I need a query to get on an other column the sum of the "current" row's value plus the previous row's value.
With the example, the result would be something like this :
Date | Value | Total
2013/11/22 | 2 | 2
2013/11/23 | 4 | 6 <--- Because 4+2 = 6
2013/11/25 | 12 | 16
2013/11/30 | 28 | 40
2013/12/02 | 2 | 30
The problem is that I can't use variables because I'm on a view.
How can I do this ?
Any help is appreciated.
SELECT
t.Date,
t.Val,
COALESCE((SELECT Val FROM Table1 sq WHERE sq.Date < t.Date ORDER BY sq.Date DESC LIMIT 1), 0) + t.Val AS whatever
FROM
Table1 t
ORDER BY t.Date
see it working live in an sqlfiddle
I have such table in my MySQL database:
---------------------------
|fid | price | date |
---------------------------
| 1 | 1.23 | 2011-08-11 |
| 1 | 1.43 | 2011-08-12 |
| 1 | 1.54 | 2011-08-13 |
| 1 | 1.29 | 2011-08-14 |
| 1 | 1.60 | 2011-08-15 |
| 1 | 1.80 | 2011-08-16 |
fid - this is the product id
price - this is the price of the product in specified day
I want to calculate average price of the product fid=1. I want to calculate the average price of first n=3 rows ordered by date for specified fid, and then calculate average price for another 3 rows ordered by date.
How can I group first 3 rows and calculate avg and then group next 3 rows and calculate avg. Before calculation I need to sort the rows by date and then group n rows.
If n=3 this should return such result:
--------------
|fid | price |
--------------
| 1 | 1.40 | 2011-08-11 -> 2011-08-13 - average price for 3 days
| 1 | 1.56 | 2011-08-14 -> 2011-08-16 - average price for 3 days
How can I create SQL Query to do such calculations?
Thanks in advance.
Unluckily mysql doesn't offer analytic functions like oracle,mssql and postgres do. So you have to play with variables to reach your goal.
create table mytest (
id int not null auto_increment primary key,
fid int,
price decimal(4,2),
fdate date
) engine = myisam;
insert into mytest (fid,price,fdate)
values
(1,1.23,'2011-08-11'),
(1,1.43,'2011-08-12'),
(1,1.54,'2011-08-13'),
(1,1.29,'2011-08-14'),
(1,1.60,'2011-08-15'),
(1,1.80,'2011-08-16');
select
concat_ws('/',min(fdate),max(fdate)) as rng,
format(avg(price),2) as average from (
select *,#riga:=#riga+1 as riga
from mytest,(select #riga:=0) as r order by fdate
) as t
group by ceil(riga/3);
+-----------------------+---------+
| rng | average |
+-----------------------+---------+
| 2011-08-11/2011-08-13 | 1.40 |
| 2011-08-14/2011-08-16 | 1.56 |
+-----------------------+---------+
2 rows in set (0.02 sec)
maybe you could use
GROUP BY FLOOR(UNIX_TIMESTAMP(date)/(60*60*24*3))
= convert to secounds, divide by secounds for 3 days, and round down
SELECT AVG( price ) FROM my_table
GROUP BY ( date - ( SELECT MIN( date ) FROM my_table WHERE fid = 1 ) ) DIV 3
WHERE fid = 1
select fid, avg(price), min(date), max(date)
from
(select floor((#rownum := #rownum + 1)/3) as `rank`, prices.*
from prices, (SELECT #rownum:=-1) as r
order by date) as t
group by rank