cumulative sum of number of records grouped by week - mysql

I have the following database schema
ID creation_date
1 2019-06-03
2 2019-06-04
3 2019-06-04
4 2019-06-10
5 2019-06-11
I need to find out the total size of the table group by week. The output I am looking for is something like
year week number_of_records
2019 23 3
2019 24 5
I am writing the following query which only gives me number of record created in each week
> select year(creation_date) as year, weekofyear(creation_date) as week,
> count(id) from input group by year, week;
Output I get is
year week number_of_records
2019 23 3
2019 24 2

Take a look to window (or analytic) functions.
Unlike aggregate functions, window functions preserve resulting rows and facilitate operations related to them. When using order by in over clause, windowing is done from first row to current row according to specified order, which is exactly what you need.
select year, week, sum(number_of_records) over (order by year, week)
from (
select year(creation_date) as year, weekofyear(creation_date) as week,
count(id) as number_of_records
from input group by year, week
) your_sql
I guess you will also need to reset sum for each year, which I leave as exercise for you (hint: partition clause).

For versions prior to 8.0...
Schema (MySQL v5.7)
CREATE TABLE my_table
(ID SERIAL PRIMARY KEY
,creation_date DATE NOT NULL
);
INSERT INTO my_table VALUES
(1 , '2019-06-03'),
(2 , '2019-06-04'),
(3 , '2019-06-04'),
(4 ,'2019-06-10'),
(5 ,'2019-06-11');
Query #1
SELECT a.yearweek
, #i:=#i+a.total running
FROM
(SELECT DATE_FORMAT(x.creation_date,'%x-%v') yearweek
, COUNT(*) total
FROM my_table x
GROUP BY yearweek
)a
JOIN (SELECT #i:=0) vars
ORDER BY a.yearweek;
| yearweek | running |
| -------- | ------- |
| 2019-23 | 3 |
| 2019-24 | 5 |
---
View on DB Fiddle

You seem to want a cumulative sum. You can do this with window functions directly in an aggregation query:
select year(creation_date) as year, weekofyear(creation_date) as week,
count(*) as number_of_starts,
sum(count(*)) over (order by min(creation_date)) as number_of_records
from input
group by year, week;

Related

How do i sum fields inside a column?

I have a table like this: Table Name: Accounting
year
acc
value
2018
in
500
2018
out
500
2019
in
600
2019
out
800
I need to show up to 10-year slots with the highest value (i.e in + out). For example, in this case, 2019 is the highest, my query should show
year
Max Value
2019
1400
My current SQL code is:
SELECT year,acc, MAX(value) as max_value
FROM Accounting
group by year,acc
LIMIT 10
How can I get the desired result?
You need SUM() and ORDER BY:
SELECT year,acc, SUM(value) as sum_value
FROM Accounting
GROUP BY year
ORDER BY sum_value DESC
LIMIT 1;
If you want only ten years to be considered, you need a WHERE clause, for instance:
SELECT year, SUM(value) as sum_value
FROM Accounting
WHERE year >= 2010
GROUP BY year
ORDER BY sum_value DESC
LIMIT 1;
I may be misunderstanding your question, but what it appears you are attempting to complete the following steps.
You are trying to SUM by year, regardless of account type, the amount in the value column.
You are trying to show only the years with the 10 highest summed values over some specified period of time.
One way to approach this would be as follows
SELECT year, SUM(value) as annual_value
FROM Accounting
GROUP BY year
ORDER BY annual_value desc
LIMIT 10
You have to use the SUM() not the MAX(), because you don't want only the highest value, but the total, and then you only have to GROUP BY them by year
SELECT year,sum(value) as max_value
FROM Accounting
group by year
LIMIT 10;
Here's the result:
+------+-----------+
| year | max_value |
+------+-----------+
| 2018 | 1000 |
| 2019 | 1400 |
+------+-----------+

can i get unique values of the aggregated field in mysql query which uses group by for another field

here is a table
id | year
1 2020
2 2020
3 2020
1 2019
2 2019
3 2019
I tried to group via year and the result i am getting is
id | year
1 2020
1 2019
but i need to get distinct values for id
Expected:
id | year
1 2020
2 2019
(or)
id | year
1 2020
3 2019
i tried with this mysql query
select * from table group by year
This might be tricky to do without using analytic functions, but if you are using MySQL 8+, here is one way:
WITH cte AS (
SELECT id, year, DENSE_RANK() OVER (ORDER BY year) dr,
ROW_NUMBER() OVER (PARTITION BY year ORDER BY id) rn
FROM yourTable
)
SELECT id, year
FROM cte
WHERE dr = rn;
Demo
For an explanation, here is a query on the full CTE without any WHERE clause:
Note that each block of year records has dr numbered from 1 onwards, and within each block of year records the rn column is numbered from 1 onwards. We retain rows where the row number and dense rank are identical.

group by date, even if there is no entry for the date

I want to visualize my entries by counting how many have been created at the same day.
SELECT dayname(created_at), count(*) FROM logs
group by day(created_at)
ORDER BY created_at desc
LIMIT 7
So I get something like:
Thursday 4
Wednesday 12
Monday 4
Sunday 1
Saturday 20
Friday 23
Thursday 10
But I also want to have the Tuesday in there with 0 so I have it for one week.
Is there a way to do this with full mysql or do I need to update the result before I can give it to the chart?
EDIT:
This is the final query:
SELECT
DAYNAME(date_add(NOW(), interval days.id day)) AS day,
count(logs.id) AS amount
FROM days LEFT OUTER JOIN
(SELECT *
FROM logs
WHERE TIMESTAMPDIFF(DAY,DATE(created_at),now()) < 7) logs
on datediff(created_at, NOW()) = days.id
GROUP BY days.id
ORDER BY days.id desc;
The table days includes numbers from 0 to -6
You only need a table of offsets which could be a real table or something built on the fly like select 0 ofs union all select -1 ....
create table days (ofs int);
insert into days (ofs) values
(0), (-1), (-2), (-3),
(-4), (-5), (-6), (-7);
select
date_add('20160121', interval days.ofs day) as created_at,
count(data.id) as cnt
from days left outer join logs data
on datediff(data.created_at, '20160121') = days.ofs
group by days.ofs
order by days.ofs;
http://sqlfiddle.com/#!9/3e6bc7/1
For performance it would probably be better to limit the search in the data (logs) table:
select
date_add('20160121', interval days.ofs day) as created_at,
count(data.id) as cnt
from days left outer join
(select * from logs where created_at between <start> and <end>) data
on datediff(data.created_at, '20160121') = days.offset
group by days.offset
order by days.offset;
One downside is that you do have to parameterize this with a fixed anchor date in a couple of expressions. It might be better to have a table of real dates sitting in a table somewhere so you don't have to do the calculations.
Use RIGHT JOIN to a dates table, so you can request data for each and all days, no matter if some days have data or not, simply, mull days will show as CERO or NULL.
You can create a dates table, some sort of calendar table.
id_day | day_date |
--------------------
1 | 2016-01-01 |
2 | 2016-01-02 |
.
.
365 | 2016-12-31 |
With this table, you can relate date, then extract day, month, week, whatever you want with MYSQL DATE AND TIME FUNCTIONS
SELECT t2.dayname(day_date), count(t1.created_at) FROM logs t1 right join dates_table t2 on t1.created_at=t2.day_date group by t2.day_date ORDER BY t1.created_at desc LIMIT 7

sort by date with sum clause

I have this table:
-----------------------------------------------------------
| id | name | date | count | balance |
-----------------------------------------------------------
1 a 0000-00-00 1 10
2 b 2014-10-02 1 20
3 c 2014-09-01 1 30
4 d 2014-09-16 1 40
I need to get the SUM of the four count & balance column on my SELECT, then order it by the date in ascending but I need to make sure it should not be the 0000-00-00. I tried this syntax but it does not order the date the way I wanted.
SELECT
date,
SUM(count) AS deposit_counts,
SUM(balance) AS balance_sum
FROM tbl
ORDER BY date ASC
My expected output:
-------------------------------------------
| date | count | balance |
-------------------------------------------
2014-09-01 4 90
Use MIN and NULLIF functions:
SELECT
MIN(NULLIF(date, '0000-00-00')) AS min_date,
SUM(count) AS deposit_counts,
SUM(balance) AS balance_sum
FROM tbl
Test it here: http://sqlfiddle.com/#!2/d43acf/1
I wish I understood the point in having a 0000-00-00 date as opposed to having null so only one possibility needs ruling out... Anyway at first I would assume you might want to get the total balances of each day and would require a query along these lines
SELECT
date,
sum(count) AS deposit_counts,
SUM(balance) AS balance_sum
FROM tbl
WHERE date <> '0000-00-00'
GROUP BY date
ORDER BY date
which would output
2014-09-01 1 30
2014-09-16 1 40
2014-10-02 1 20
but as it stands it appears you don't, I'm not one hundred percent sure what your ultimate goal is in terms of future querying so I will guess you want a historical value of balances up to the current date, ruling out the weird zero date thing. In which case you might want a greater than WHERE clause, and presumably just the lowest value in the date column to show the total from this date like so:
SELECT
min(date) AS date,
sum(count) AS deposit_counts,
SUM(balance) AS balance_sum
FROM tbl
WHERE date > '0000-00-00'
since it would ignore the invalid date it will return
2014-09-01 3 80
I hope this is of some use.
Edit:
if you absolutely require all values then you will want to use a subquery to retrieve and rule out the exceptional date result like so:
SELECT
(SELECT min(date) FROM tbl WHERE date > '0000-00-00') AS date,
sum(count) AS deposit_counts,
SUM(balance) AS balance_sum
FROM tbl
or as above, nullif on your date query should work too

MySQL query to return total Profit/Loss for a list of dates

I have a MySQL table with 4 columns as follows.
TransactionID | Item | Amount | Date
------------------------------------
| | |
| | |
I have a lot of entries and what I want to be able to do is create a query that returns a list of the total Profit/Loss at each point in time?
Imagine I had the following transactions:
Bought item for 5
Sold item for 15
Bought item for 5
Sold item for 15
So I would want it to return something like this.
Profit/Loss | Date
------------------
-5 | 20-10-12
10 | 21-10-12
5 | 22-10-12
20 | 23-10-12
Is this possible with a MySQL query?
SELECT SUM(Amount) AS ProfitOrLoss FROM TableName GROUP BY Date
Assuming that Date is stored as you show on the expected result this should work:
SELECT
SUM(Amount) AS "Profit/Loss",
Date
FROM your_table
GROUP BY(Date)
Otherwise id Date is of type DATE, DATETIME or TIMESTAMP you could do something like this:
SELECT
SUM(Amount) AS "Profit/Loss",
DATE_FORMAT(Date, '%d-%m-%y') AS Date
FROM your_table
GROUP BY(DATE_FORMAT(Date, '%d-%m-%y'))
references:
DATE_FORMAT
GROUP BY
EDIT (after OP's comment)
to achieve the comulative SUM here is a good hint:
SET #csum := 0;
SELECT
(#csum := #csum + x.ProfitLoss) as ProfitLoss,
x.Date
FROM
(
SELECT
SUM(Amount) AS ProfitLoss,
DATE_FORMAT(Date, '%d-%m-%y') AS Date
FROM your_table
GROUP BY(DATE_FORMAT(Date, '%d-%m-%y'))
) x
order by x.Date;
essentialy you store the current sum into a variable (#csum) and for each row of the grouped transactions you increase it by the daily balance
SELECT SUM(Amount) FROM TableName GROUP BY Date