MySQL: Calculate 4 Week Average with 4 Week Offset - mysql

This question is related to a post I made earlier: MySQL: Calculating Data from Table with One Month Offset
But now I need to build a procedure that queries a table of contact data stored by week. Here's a simplified example of the table I am working with:
+-----------------+------------+
| week_start_date | contacts |
+-----------------+------------+
| 2015-03-08 | 12 |
| 2015-03-01 | 20 |
| 2015-02-22 | 5 |
| 2015-02-15 | 17 |
| 2015-02-08 | 8 |
| 2015-02-01 | 2 |
| 2015-01-25 | 16 |
| 2015-01-18 | 10 |
| 2015-01-11 | 4 |
| ... | ... |
+-----------------+------------+
What I need to figure out is how to calculate a 4 week moving average that also has a 4 week offset on top of that. For instance, if I wanted to get the average contacts for the week of March 8, 2015, it would be the average of January 18 through February 8. In the example above, my average would be: (10 + 16 + 2 + 8 ) / 4 = 9. And if I wanted to find the average for the week of March 1, 2015, then it would be the average of January 11 through February 1 which comes out to be 8 using the sample table above.
From my last post, I know that I can handle the 4 week offset by joining the table with itself on the week_start_date similar to this:
SELECT s1.week_start_date, s2.Total_Contacts
FROM sample_table s1
LEFT JOIN (SELECT week_start_date, sum(contacts) AS Total_Contacts
FROM sample_table
GROUP BY week_start_date) s2
ON s1.week_start_date =
date_add(s2.week_start_date, INTERVAL 4 WEEK)
WHERE s1.week_start_date = '2015-03-08'
GROUP BY s1.week_start_date;
But getting it to compute the four week average as well is where I am stuck. I thought joining it on a range of dates would work, but I keep getting averages that are a lot larger than expected. I'm guessing it is due to how the week_start_date's are being grouped. (Note that there can be multiple records for each week. I only show one record for each week on the sample table to make it less cluttered.)
Is joining on a date range the correct approach? Or do I need to add another join somewhere?
Thanks for your help.

I would suggest using a correlated subquery:
select st.*,
(select avg(contacts)
from sample_table st2
where st2.week_start_date >= st.week_start_date - interval 7 * 7 days and
st2.week_start_date <= st.week_start_date - interval 4 * 7 days
) as avg_4week_delayed
from sample_table st;

I would use the DATE_SUB() function and just subtract the necessary weeks you need. So, for March 8 in your example, try something like this:
SELECT AVG(contacts)
FROM myTable
WHERE week_start_date <= DATE_SUB('2015-03-08', INTERVAL 4 WEEK) AND week_start_date >= DATE_SUB('2015-03-08', INTERVAL 7 WEEK);
It worked in SQL Fiddle.

Related

displays the data for the last 3 months available in the table MYSQL

I have the following invoice data :
tbl_invoice :
+------------+--------+
| date | no_inv |
+------------+--------+
| 2021-01-02 | INV1 |
| 2015-01-02 | INV2 |
| 2013-01-01 | INV2 |
| 2021-01-05 | INV4 |
+------------+--------+
I want to display the last 3 months of data available even though it is different for different years, because my expected result is the last 3 months of data.
My Expectation Result :
In the invoice table from this figure, only date data are available in April 2020, May 2019 and June 2015.
I want to get the data for the last 3 months. How can SQL get results according to my expectations?
Assuming your date column is a datetime type, you can do this with a simple where.
You need to calculate the date 3 months ago, and filter to where date is greater than that. e.g.:
WHERE date > DATE_SUB(NOW(), INTERVAL 3 MONTH);
See the datetime arithmetic functions here:
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html
Solved with :
SELECT date
FROM invoice
GROUP BY MONTH(date)
ORDER BY date DESC
LIMIT 3

How to select some rows as columns in mysql

Hello guys? I am working with a MySQL table that stores employees payno, date and meter readings for electricity. I want to select readings for a previous month as previous month reading and current month reading as current month reading, this is what I mean
payno | date | value
-------+------------+------------
6 | 2019-08-31 | 2477
6 | 2019-09-25 | 2487
8 | 2019-08-31 | 1651.2
8 | 2019-09-25 | 1697.6
Above is the structure of my table, I want to achieve something like this:
payno | previous month| current month
-------+---------------+------------
6 | 2477 | 2487
8 | 1651.2 | 1697.6
Notice that the previous month reading are the readings having month(date) as 8 and the current month readings have month(date) as 9.
I don't know how many readings can be per month, so we'll summarize them.
select e.payno,
( select sum(x1.val)
from emp x1
where x1.payno=e.payno and month(x1.data)=month(date_sub(e.data, INTERVAL 1 MONTH))
group by x1.payno ) AS prev_month,
( select sum(x2.val)
from emp x2
where x2.payno=e.payno and month(x2.data)=month(e.data)
group by x2.payno ) AS this_month
from emp e
where month(e.data)=9 /* this is where you specify "current" month */
;

Generate resultset according to report

How do I generate the following reports using MYSQL?
My table 'reservations' looks like this:
id | arrival | created.
1 | 2014-3-30 | 2014-3-1
2 | 2014-3-31 | 2014-3-2
3 | 2014-3-28 | 2014-3-2
4 | 2014-3-01 | 2014-3-1
5 | 2014-3-01 | 2014-3-1
I want to generate the following two aggregation reports for the "arrival" column for the whole month as shown below:
(1)
arrival | count | total_monthly_arrivals
2014-03-01 | 2 | 5
2014-03-02 | 0 | 5
2014-03-03 | 0 | 5
...
2014-03-30 | 1 | 5
2014-03-31 | 1 | 5
(2)
January | 5
...
March | 5
...
December | 0 | 5
I want these 2 result sets. It generates date according & month according report and generate result set in these form.
I tried to use group by with count in first resultset but it doesn't retrieve for a date that does not exist. Again I wanna put month condition such that I choose month. where month = '02' or something like this. Is that possible?
My SQLFiddle should answer both parts of your question: http://sqlfiddle.com/#!2/9f130/31
This will return the arrival date with a count of how many people are coming that day and how many monthly arrivals there are
select distinct(r.arrival) as arrival_date,
count(r.arrival) as total_per_day,
sa.month_total as total_arrival_per_month
from reservations as r
,(select substr(arrival,6,2) as month,
count(substr(arrival,6,2)) as month_total
from reservations
group by month) as sa
where substr(r.arrival,6,2) = sa.month
group by arrival_date,total_arrival_per_month;
This will return the month of the year and how many people are booked for that month and how many are arriving that month. (Updated with the suggestion from agrizzo.)
select MONTHNAME(STR_TO_DATE(substr(r.arrival,6,2), '%m')) as arrival_date,
sa.month_total as total_arrival_per_month
from reservations as r
,(select substr(arrival,6,2) as month,
count(substr(arrival,6,2)) as month_total
from reservations
group by month) as sa
where substr(r.arrival,6,2) = sa.month
group by arrival_date,total_arrival_per_month;
There is however, no way for me to give you every day/month of the year without a fully qualified data set, provided by and prefilled by you. That's on you to do and provide us with.
However, you can check this thread. Get a list of dates between two dates and leverage their information with my queries to get your desired results.

Highest entry from group by and compare to another value using having

This is what I want:
Look if the highest created from the same FK is more than X days old.
This is how the data is structured (this is not the result from the query below):
table_1
id | FK_table_2 | created
-------------------------------------------------------
1 | 20 | 2013-11-12 12:13:14
2 | 20 | 2013-11-12 11:10:12
3 | 21 | 2013-10-02 12:53:20
4 | 21 | 2013-09-02 12:54:20
Note: Doing a subquery will be to slow.
What I come up with is:
SELECT *
FROM table_1
GROUP BY FK_table_2
HAVING MAX(created) < NOW() - INTERVAL 3 DAY
I'm worried that HAVING MAX(created) has not garantuee to use the highest created.
Is there any other ways to do this?
I don't think you need to use a MAX clause, you can surely just select all rows older than 3 days and do your work on them.
SELECT DISTINCT FK_table_2 FROM table_1
WHERE created > NOW() - INTERVAL 3 DAY;
Update this won't work :-(
try this
SELECT FK_table_2,max(created)
FROM table_1
where created < NOW() - INTERVAL 3 DAY
GROUP BY FK_table_2

Mysql query to get count for last week

I'm trying to get a count from last week visits.
Up to now, i have this working:
SELECT FROM_UNIXTIME(login),COUNT(*)
FROM users
WHERE FROM_UNIXTIME(login) >= (CURDATE() - INTERVAL DAYOFWEEK(CURDATE())+6 DAY)
GROUP BY DAYOFWEEK(FROM_UNIXTIME(login));
Results:
+----------------------+----------+
| FROM_UNIXTIME(login) | COUNT(*) |
+----------------------+----------+
| 2013-04-08 12:49:04 | 1 |
| 2013-04-10 17:29:21 | 2 |
| 2013-04-05 21:27:00 | 1 |
+----------------------+----------+
Problems:
-Table is not ordered by date;
-I'd like to show all 7 rows, even if count value='0'.
How can i fix this? Thanks in advance!
[UPDATE]
Order by date solved with:
ORDER BY FROM_UNIXTIME(login);
Just need to show all 7 rows of week!
You can use the query from this answer which can generate a list of dates, and then left join your query against it:
generate days from date range