Cumulative sum grouped by year, month and day in a JSON object - json

Let's say I have a table orders with the following rows:
ID Cost Date (timestamp)
1 100 2020-06-30 21:18:53.328386+00
2 45 2020-06-30 11:18:53.328386+00
3 200 2020-05-29 21:32:56.620174+00
4 20 2020-06-28 21:32:56.620174+00
And I need a query that returns exactly this:
Month Year Costs
5 2020 {"1": 0, "2": 0, ..., "29": 200, "30": 200, "31": 200}
6 2020 {"1": 0, "2": 0, ..., "28": 20, "29": 20, "30": 165}
Please note that the column Costs has to be a json with the key being the day in the month and the value being the cumulative sum of all previous days in that month.
I know this is probably not a task that postgres should be doing, but I'm just curious to see what is the solution to it (even if its not the most efficient in production environments)

You can use two levels of aggregation and json_object_agg():
select date_month, date_year, json_object_agg(date_day, cnt) costs
from (
select
extract(month from date) date_month,
extract(year from date) date_year,
extract(day from date) date_day,
sum(sum(cost)) over(
partition by extract(month from date), extract(year from date)
order by extract(day from date)
) cnt
from mytable
group by 1, 2, 3
) t
group by date_month, date_year

Related

Combine two queries and add a new column

DB-Fiddle
CREATE TABLE sales (
id int auto_increment primary key,
category VARCHAR(255),
event_date DATE,
sent_date DATE,
sales_Product_gross VARCHAR(255),
return_Product_gross VARCHAR(255)
);
INSERT INTO sales
(category, event_date, sent_date,
sales_Product_gross, return_Product_gross
)
VALUES
("CAT_01", "2017-05-30", "2017-05-30", "500", NULL),
("CAT_01", "2017-06-05", "2017-05-30", NULL, "250"),
("CAT_01", "2018-07-08", "2018-07-08", "700", NULL),
("CAT_01", "2018-07-18", "2018-07-08", NULL, "370"),
("CAT_01", "2019-02-15", "2019-02-15", "400", NULL),
("CAT_01", "2019-03-21", "2019-02-15", NULL, "120"),
("CAT_02", "2019-04-24", "2019-04-24", "300", NULL),
("CAT_02", "2019-04-30", "2019-04-24", NULL, "145"),
("CAT_02", "2019-12-14", "2019-12-14", "900", NULL),
("CAT_02", "2019-12-28", "2019-12-14", NULL, "340"),
("CAT_03", "2020-03-09", "2020-03-09", "800", NULL),
("CAT_03", "2020-03-17", "2020-03-09", NULL, "425");
The table displays the sales and returns in different categories.
Now, I want to calculate:
a) the return_rate per month per campaign and store it in a new column called calc_type with the name monthly.
b) the return_rate on a rolling 2 YEAR basis and also store it in the new column calc_type with the name rolling.
The result should look like this:
category calc_type year month return_rate
CAT_01 rolling NULL NULL 0.445
CAT_01 monthly 2017 5 0.500
CAT_01 monthly 2018 7 0.528
CAT_01 monthly 2019 2 0.300
CAT_02 rolling NULL NULL 0.404
CAT_02 monthly 2019 4 0.480
CAT_02 monthly 2019 12 0.377
CAT_03 rolling NULL NULL 0.531
CAT_03 monthly 2020 3 0.531
I have created a query for criteria a) and for criteria b). Separately, those queries work exactly the way I need it.
Now, I tried to combine them using UNION ALL the same way it is done here:
SELECT
category,
'rolling' AS calc_type,
'NULL' AS year,
'NULL' As month,
sum(return_Product_gross) / sum(sales_Product_gross) as return_rate
FROM sales
WHERE sent_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 2 YEAR) AND CURDATE()
GROUP BY 1,2,3,4
ORDER BY 1,2,3,4;
UNION ALL
SELECT
category,
'monthly' AS calc_type,
YEAR(sent_date) AS year,
MONTH(sent_date) AS month,
sum(return_Product_gross) / sum(sales_Product_gross) as return_rate
FROM sales
WHERE sent_date BETWEEN "2017-01-01" AND CURDATE()
GROUP BY 1,2,3,4
ORDER BY 1,2,3,4;
However, now only the values for rolling are displayed in the result.
What do I need to change in my queries to get the expected result?
This query looks worked:
SELECT
category,
'rolling' AS calc_type,
'NULL' AS year,
'NULL' As month,
sum(return_Product_gross) / sum(sales_Product_gross) as return_rate
FROM sales
WHERE sent_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 2 YEAR) AND CURDATE()
GROUP BY category, year, month
UNION ALL
SELECT
category,
'monthly' AS calc_type,
YEAR(sent_date) AS year,
MONTH(sent_date) AS month,
sum(return_Product_gross) / sum(sales_Product_gross) as return_rate
FROM sales
WHERE sent_date BETWEEN "2017-01-01" AND CURDATE()
GROUP BY category, year, month
ORDER BY category, calc_type DESC, year, month;
DBFiddle

SQL Query:- Difference prices of same month with same year with different tables , if not just show zero

There are two different tables, just need to subtract price between same month with same year, if no data just show zero for that particular month and year .Now, it just subtracting with row by row irrespective of month and year.
Table 1 Table2
Price tran_date Price post_date
60 2018-01-01 30 2018-01-15
40 2018-02-08 30 2018-02-02
50 2018-12-28 30 2018-11-01
40 2019-03-01 10 2019-01-08
80 2019-04-11 60 2019-04-29
40 2019-10-01
Expected Answer:
Sum(price). Year
30 January 2018
10 February 2018
30 November 2018
50 December 2018
-10 January 2019
40 March 2019
20 April 2019.
40 October 2019
Actual Answer:
Sum(Price) Year
30 January 2018
10 February 2018
10 December 2018
30 March 2019
20 April 2019
-40 October 2019
SQL Query for table1
Select sum(price) from table1 where date(tran_date)
between ‘2018-01-01’ and ‘2019-12-31’
group by month(tran_date),year(tran_date)
SQL Query for table2
Select sum(price) from table2 where date(post_date)
between ‘2018-01-01’ and ‘2019-12-31’
group by month(post_date),year(post_date)
It’s should not subtract from 1st row of table1 with 1st row of table2,it should subtract with same month with same year. If there is no data just show zero for that particular month and year.
Please do help.Thanks in Advance.
seems you want the absolute difference, try add abs()
sample
select date_year, date_month,
abs(sum(price))
from ((select date_year, date_month, price from
(values (60, '2018', '01'),
(40, '2018', '02'),
(50, '2018', '12'),
(40, '2019', '03'),
(80, '2019', '04') ) table1 (price, date_year, date_month)
) union all
(select date_year, date_month, - price from (
values (30, '2018', '01'),
(30, '2018', '02'),
(30, '2018', '11'),
(10, '2019', '01'),
(60, '2019', '04'),
(40, '2019', '10')
) table2 (price, date_year, date_month)
)
) t
group by date_year, date_month
order by date_year, date_month
see the fiddle
https://www.db-fiddle.com/f/qVQYB2KXSTbJNEkSH1oGuG/0
Is this what you want?
select year(dte), month(dte),
greatest( sum(price), 0)
from ((select tran_date as dte, price from table1
) union all
(select post_date, - price from table2
)
) t
group by year(dte), month(dte);
It seems very strange to not subtract the values. I suspect you might just want:
select year(dte), month(dte),
sum(price)
from ((select tran_date as dte, price from table1
) union all
(select post_date, - price from table2
)
) t
group by year(dte), month(dte)

query to find count of records on daily basis for given time range

I need to count no of records in a table daily between 9 AM to 6 PM on a given date range.
Sample output should look like:
Date Count of users
01-11-2018 100
02-11-2018 88
03-11-2018 107
04-11-2018 113
SELECT DATE(date), count(*)
FROM table
WHERE TIME(date) BETWEEN TIME('9:00:00') AND TIME('18:00:00')
GROUP BY DATE(date)
SELECT `date`,
Count(*)
FROM table_name tn
WHERE tn.date >= Timestampadd(hour, 9, Curdate())
AND tn.date <= Timestampadd(hour, 18, Curdate())
GROUP BY `date`

grouping by week mysql

i have this sql which selects data from the database and returns two columns
for this sql,
SELECT count(*) cnt, date(created) dt FROM mlm_users GROUP by dt
i get something line
count, date
1, 2013-05-10
2, 2013-06-10
11, 2013-09-10
for the following,
SELECT count(*) cnt, week(DATE_SUB(created, INTERVAL 1 DAY)) dt
FROM mlm_users GROUP by dt
i get
count, week no
1, 12
2, 22
11, 34
is there anyway i can return the week date range like
count, week no
1, 2013-01-12 - 2013-07-12
2, 2013-08-12 - 2013-14-12
11, 2013-15-12 - 2013-29-12
and for the mnonth return thw month
SELECT month( created ) dt, date(created) dt FROM mlm_users GROUP by dt
i could have calculated this value manually, but because of minor issues in date calculation, i wanted to know if the database can give this value.
pls see http://sqlfiddle.com/#!2/cd3db/2
thanks
Maybe something like this,if I understand you
http://sqlfiddle.com/#!2/cd3db/18

MySQL generating monthly breakdown from date ranges

good morning,
I have a nagging issue I cannot really solve.. I have a database table like this, showing resources spent (value) in a date range by person:
id,name,startdate,enddate,value
--------------------------------
10,John,2012-01-14,2012-10-30,200000
11,Jack,2012-02-01,2012-08-01,70000
12,John,2012-05-01,2012-06-01,2000
I need a query that creates the result like this, summarizing the 'value' by month, taking partial months into account
month, name, value
------------------
2012-01, John, 9000
2012-02, John, 18000
2012-03, John, 18000
2012-04, John, 18000
2012-05, John, 20000
2012-06, John, 18000
2012-07, John, 18000
2012-08, John, 18000
2012-01, John, 18000
2012-02, Jack, 10000
2012-03, Jack, 10000
2012-04, Jack, 10000
2012-05, Jack, 10000
2012-06, Jack, 10000
2012-07, Jack, 10000
2012-08, Jack, 0
Now I know how I'd do this procedurally (like with PHP) with a loop: get the daily amount, then check month by month how many days spent there according to the range and multiply it by the daily amount.
thanks
peter
If you don't have a calendar table and can't create one, you can simulate a virtual calendar table in your query. Here's a query that should answer your question, that makes use of such a virtual table:
select m.startmonth,
e.name,
coalesce(sum(r.value *
datediff(case when adddate(m.startmonth, interval 1 month) <
r.enddate
then adddate(m.startmonth, interval 1 month)
else r.enddate end,
case when m.startmonth > r.startdate
then m.startmonth else r.startdate end) /
datediff(r.enddate,r.startdate)),0) valueshare
from
(select cast('2012-01-01' as date) startmonth union all
select cast('2012-02-01' as date) startmonth union all
select cast('2012-03-01' as date) startmonth union all
select cast('2012-04-01' as date) startmonth union all
select cast('2012-05-01' as date) startmonth union all
select cast('2012-06-01' as date) startmonth union all
select cast('2012-07-01' as date) startmonth union all
select cast('2012-08-01' as date) startmonth union all
select cast('2012-09-01' as date) startmonth union all
select cast('2012-10-01' as date) startmonth) m
cross join employees e
left join resources_spent r
on r.enddate > m.startmonth and
r.startdate < adddate(m.startmonth, interval 1 month) and
r.name = e.name
group by m.startmonth, e.name
order by 2,1
SQLFiddle here.
I think you need a calendar table with one row for each date. Other fields would be whatever is useful to you, such as fiscal periods, holidays, whatever.
Then, for your report, you could create a temp table and populate it like this:
insert into YourTempTable
(id, date, amount)
select id, c.datefield, amount
from YourTable join Calendar c on datefield >= startdate
and datefield <= enddate
where whatever
From there, you select from YourTempTable and YourTable, joining on the id.