i have a table main:
(
time date,
qty int
)
i want to create a query so for each day i get the sum of qty on that day and all days before that
so for this data
-----------------------
time | qty
01/09/2009 | 3
02/09/2009 | 8
03/09/2009 | 2
04/09/2009 | 5
i get:
-----------------------
time | total
01/09/2009 | 3
02/09/2009 | 11
03/09/2009 | 13
04/09/2009 | 18
thanks in advance
This should give you a faster result
SELECT time
, #tot_qty := #tot_qty+qty AS tot_qty
FROM Table1
JOIN (SELECT #tot_qty := 0) d
order by time
SQL FIDDLE
SELECT
M.Time,
SUM(M2.Qty) RunningTotal
FROM
Main M
LEFT JOIN Main M2 ON M.Time >= M2.Time
GROUP BY
M.Time;
SELECT TIME, (SELECT SUM(QTY) FROM main m2 WHERE m2.ITME <= mt1.TIME) AS sum
FROM main m1
ORDER BY TIME
This should do the trick, though it might not be the fastest solution.
Related
I've searched for this topic but all I got was questions about grouping results by month. I need to retrieve rows grouped by month with summed up cost from start date to this whole month
Here is an example table
Date | Val
----------- | -----
2017-01-20 | 10
----------- | -----
2017-02-15 | 5
----------- | -----
2017-02-24 | 15
----------- | -----
2017-03-14 | 20
I need to get following output (date format is not the case):
2017-01-20 | 10
2017-02-24 | 30
2017-03-14 | 50
When I run
SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)
I got sum per month of course.
Nothing comes to my mind how to put in nicely in one query to achieve my desired effect, probably W will need to do some nested queries but maybe You know some better solution.
Something like this should work (untestet). You could also solve this by using subqueries, but i guess that would be more costly. In case you want to sort the result by the total value the subquery variant might be faster.
SET #total:=0;
SELECT
(#total := #total + q.sum) AS total, q.date
FROM
(SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)) AS q
You can use DATE_FORMAT function to both, format your query and group by.
DATE_FORMAT(date,format)
Formats the date value according to the format string.
SELECT Date, #total := #total + val as total
FROM
(select #total := 0) x,
(select Sum(Val) as Val, DATE_FORMAT(Date, '%m-%Y') as Date
FROM st where Date >= '2017-01-01' and Date <= '2017-12-31'
GROUP BY DATE_FORMAT(Date, '%m-%Y')) y
;
+---------+-------+
| Date | total |
+---------+-------+
| 01-2017 | 10 |
+---------+-------+
| 02-2017 | 30 |
+---------+-------+
| 03-2017 | 50 |
+---------+-------+
Can check it here: http://rextester.com/FOQO81166
Try this.
I use yearmonth as an integer (the year of the date multiplied by 100 plus the month of the date) . If you want to re-format, your call, but integers are always a bit faster.
It's the complete scenario, including input data.
CREATE TABLE tab (
dt DATE
, qty INT
);
INSERT INTO tab(dt,qty) VALUES( '2017-01-20',10);
INSERT INTO tab(dt,qty) VALUES( '2017-02-15', 5);
INSERT INTO tab(dt,qty) VALUES( '2017-02-24',15);
INSERT INTO tab(dt,qty) VALUES( '2017-03-14',20);
SELECT
yearmonths.yearmonth
, SUM(by_month.month_qty) AS running_qty
FROM (
SELECT DISTINCT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
FROM tab
) yearmonths
INNER JOIN (
SELECT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
, SUM(qty) AS month_qty
FROM tab
GROUP BY YEAR(dt) * 100 + MONTH(dt)
) by_month
ON yearmonths.yearmonth >= by_month.yearmonth
GROUP BY yearmonths.yearmonth
ORDER BY 1;
;
yearmonth|running_qty
201,701| 10.0
201,702| 30.0
201,703| 50.0
select succeeded; 3 rows fetched
Need explanations?
My solution has the advantage over the others that it will be re-usable without change when you move it to a more modern database - and you can convert it to using analytic functions when you have time.
Marco the Sane
my products table :
ProductId(inc.key) | Price | VersionCreatedDate | MainProductId
1 | 15 | 1-11-2016 | 1
2 | 20 | 1-11-2016 | 2
3 | 30 | 1-11-2016 | 3
4 | 10 | 2-11-2016 | 1 -> mainProductId 1 changed price(-5$)
5 | 20 | 3-11-2016 | 3 -> mainProductId 3 changed price(-10$)
6 | 30 | 4-11-2016 | 3 -> mainProductId 3 changed price(+10$)
I want to display the output as like this
Date | AvgPrice
1-11-2016 | 21.67 ((15+20+30)/3)
2-11-2016 | 20 ((10+20+30)/3)
3-11-2016 | 16.67 ((10+20+20)/3)
4-11-2016 | 20 ((10+20+30)/3)
How do I get the output with sql code?
Assuming you have a calendar table with all dates you need. And you have a main_products table with MainProductId as primary/unique key. The following query should return average prices for every day in october 2016.
select sub.date, avg(sub.Price) as Price
from (
select
c.date,
m.MainProductId,
(
select p.Price
from products
where p.MainProductId = m.MainProductId
and p.VersionCreatedDate < c.date + interval 1 day
order by p.VersionCreatedDate desc
limit 1
) as Price
from callendar c
cross join main_products m
where c.date between '2016-10-01' and '2016-10-31'
) sub
group by sub.date
order by sub.date
The subquery (derived table aliased as sub) returns a combination of all dates in the range and all "main products" from the main_products table. The recent price each "main product" for a specific date is calculated in the subselect (correlated subquery in the SELECT clause) using ORDER BY and LIMIT 1. This allows us to group the subquery result by date and calculate the average price per date.
It is even possible to eliminate the derived table and hope that mysql can use an index to GROUP BY date instead of working on a temp table:
select c.date, avg((
select p.Price
from products
where p.MainProductId = m.MainProductId
and p.VersionCreatedDate < c.date + interval 1 day
order by p.VersionCreatedDate desc
limit 1
)) as Price
from callendar c
cross join main_products m
where c.date between '2016-10-01' and '2016-10-31'
group by c.date
order by c.date
I have no clue if that query can be executed effiently (especially if mysql can). You should however have at least the following indexes: callendar(date), products(MainProductId, VersionCreatedDate)
I have an MySQL table, similar to this example:
c_id date value
66 2015-07-01 1
66 2015-07-02 777
66 2015-08-01 33
66 2015-08-20 200
66 2015-08-21 11
66 2015-09-14 202
66 2015-09-15 204
66 2015-09-16 23
66 2015-09-17 0
66 2015-09-18 231
What I need to get is count of periods where dates are in row. I don't have fixed start or end date, there can be any.
For example: 2015-07-01 - 2015-07-02 is one priod, 2015-08-01 is second period, 2015-08-20 - 2015-08-21 is third period and 2015-09-14 - 2015-09-18 as fourth period. So in this example there is four periods.
SELECT
SUM(value) as value_sum,
... as period_count
FROM my_table
WHERE cid = 66
Cant figure this out all day long.. Thx.
I don't have enough reputation to comment to the above answer.
If all you need is the NUMBER of splits, then you can simply reword your question: "How many entries have a date D, such that the date D - 1 DAY does not have an entry?"
In which case, this is all you need:
SELECT
COUNT(*) as PeriodCount
FROM
`periods`
WHERE
DATE_ADD(`date`, INTERVAL - 1 DAY) NOT IN (SELECT `date` from `periods`);
In your PHP, just select the "PeriodCount" column from the first row.
You had me working on some crazy stored procedure approach until that clarification :P
I should get deservedly flamed for this, but anyway, consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(date DATE NOT NULL PRIMARY KEY
,value INT NOT NULL
);
INSERT INTO my_table VALUES
('2015-07-01',1),
('2015-07-02',777),
('2015-08-01',33),
('2015-08-20',200),
('2015-08-21',11),
('2015-09-14',202),
('2015-09-15',204),
('2015-09-16',23),
('2015-09-17',0),
('2015-09-18',231);
SELECT x.*
, SUM(y.value) total
FROM
( SELECT a.date start
, MIN(c.date) end
FROM my_table a
LEFT
JOIN my_table b
ON b.date = a.date - INTERVAL 1 DAY
LEFT
JOIN my_table c
ON c.date >= a.date
LEFT
JOIN my_table d
ON d.date = c.date + INTERVAL 1 DAY
WHERE b.date IS NULL
AND c.date IS NOT NULL
AND d.date IS NULL
GROUP
BY a.date
) x
JOIN my_table y
ON y.date BETWEEN x.start AND x.end
GROUP
BY x.start;
+------------+------------+-------+
| start | end | total |
+------------+------------+-------+
| 2015-07-01 | 2015-07-02 | 778 |
| 2015-08-01 | 2015-08-01 | 33 |
| 2015-08-20 | 2015-08-21 | 211 |
| 2015-09-14 | 2015-09-18 | 660 |
+------------+------------+-------+
4 rows in set (0.00 sec) -- <-- This is the number of periods
there is a simpler way of doing this, see here SQLfiddle:
SELECT min(date) start,max(date) end,sum(value) total FROM
(SELECT #i:=#i+1 i,
ROUND(Unix_timestamp(date)/(24*60*60))-#i diff,
date,value
FROM tbl, (SELECT #i:=0)n WHERE c_id=66 ORDER BY date) t
GROUP BY diff
This select groups over the same difference between sequential number and date value.
Edit
As Strawberry remarked quite rightly, there was a flaw in my apporach, when a period spans a month change or indeed a change into the next year. The unix_timestamp() function can cure this though: It returns the seconds since 1970-1-1, so by dividing this number by 24*60*60 you get the days since that particular date. The rest is simple ...
If you only need the count, as your last comment stated, you can do it even simpler:
SELECT count(distinct diff) period_count FROM
(SELECT #i:=#i+1 i,
ROUND(Unix_timestamp(date)/(24*60*60))-#i diff,
date,value
FROM tbl,(SELECT #i:=0)n WHERE c_id=66 ORDER BY date) t
Tnx. #cars10 solution worked in MySQL, but could not manage to get period count to echo in PHP. It returned 0. Got it working tnx to #jarkinstall. So my final select looks something like this:
SELECT
sum(coalesce(count_tmp,coalesce(count_reserved,0))) as sum
,(SELECT COUNT(*) FROM my_table WHERE cid='.$cid.' AND DATE_ADD(date, INTERVAL - 1 DAY) NOT IN (SELECT date from my_table WHERE cid='.$cid.' AND coalesce(count_tmp,coalesce(count_reserved,0))>0)) as periods
,count(*) as count
,(min(date)) as min_date
,(max(date)) as max_date
FROM my_table WHERE cid=66
AND coalesce(count_tmp,coalesce(count_reserved,0))>0
ORDER BY date;
I have a table with numbers and dates (1 number each date and dates aren't necessarily at regular intervals).
I would like to get the count of dates when a number isn't in the table.
Where I am :
select *
from
(
select
date from nums
where chiffre=1
order by date desc
limit 2
) as f
I get this :
date
--------------
2014-09-07
--------------
2014-07-26
Basically, I have this query dynamically:
select * from nums where date between "2014-07-26" and "2014-09-07"
And in a second time, browse the whole table (because there I limited to the first 2 rows but I would compare the 2 and 3 and 3 and 4 etc...)
The goal is to get this:
date | actual_number_of_real_dates_between_two_given_dates
2014-09-07 - 2014-07-26 | 20
2014-04-02 - 2014-02-12 | 13
etc...
How can I do this? Thanks.
Edit:
What I have (just an example, dates and "chiffre" are more complex) :
date | chiffre
2014-09-30 | 2
2014-09-29 | 1
2014-09-28 | 2
2014-09-27 | 2
2014-09-26 | 1
2014-09-25 | 2
2014-09-24 | 2
etc...
What I need for the number "1":
actual_number_of_real_dates_between_two_given_dates
1
3
etc...
Edit 2:
My updated query thanks to Gordon Linoff
select count(n.id) as difference
from nums n inner join
(select min(date) as d1, max(date) as d2
from (select date from nums where chiffre=1 order by date desc limit 2) d
) dd
where n.date between dd.d1 and dd.d2
How can I test row 2 with 3? 3 with 4 etc... Not only last 2?
Should I use a loop? Or I can do it without?
Does this do what you want?
select count(distinct n.date) as numDates,
(datediff(dd.d2, dd.d1) + 1) as datesInPeriod,
(datediff(dd.d2, dd.d1) + 1 - count(distinct n.date)) as missingDates
from nums n cross join
(select date('2014-07-26') as d1, date('2014-09-07') as d2) d
where n.date between dd.d1 and dd.d2;
EDIT:
If you just want the last two dates:
select count(distinct n.date) as numDates,
(datediff(dd.d2, dd.d1) + 1) as datesInPeriod,
(datediff(dd.d2, dd.d1) + 1 - count(distinct n.date)) as missingDates
from nums n cross join
(select min(date) as d1, max(date) as d2
from (select date from nums order by date desc limit 2) d
) dd
where n.date between dd.d1 and dd.d2;
i have something like this:
id | value
---------------
201311 | 10
201312 | 15
201401 | 20
201402 | 5
201403 | 17
and i need a result like this:
201311 | NULL or 0
201312 | 3.3 // 10/3
201401 | 8.3 // (15+10)/3
201402 | 15 // (20+15+10)/3
201403 | 13.3 // (5+20+15)/3
So far, i got to the point where i can get the AVG of the last three previous rows like this:
select AVG(c.value) FROM (select b.value from table as b where b.id < 201401 order by b.id DESC LIMIT 3) as c
passing the id manually. I'm not able to do it for each id.
Any ideas would be much appreciated!
thanks a lot.
regards
I think you'll have to write a stored procedure, use a cursor, iterate through the table and populate a new table using the values calculated in your cursor loop. If you need help with writing out the cursor loop, just drop a comment and I can get you an example.
i got to this now:
SELECT a.id, (select AVG(b.value) FROM table as b where b.id < a.id AND str_to_date(CONCAT(b.id,'01'), '%Y%m%d') >= DATE_SUB(str_to_date(CONCAT(a.id,'01'), '%Y%m%d'), INTERVAL 3 MONTH)) FROM `table` as a WHERE 1
But i'm quite sure there should be a better/cleaner solution
select a.id,coalesce(b.value,0) from test a left outer join
(select a.id, sum(b.value)/3 as value from
(select #row:=#row+1 as rownum,id,value from test,(select #row:=0)r) a,
(select #row1:=#row1+1 as rownum,id,value from test,(select #row1:=0)r) b
where b.rownum in (a.rownum-1,a.rownum-2,a.rownum-3)
group by a.rownum) b
on a.id=b.id;