Moving average in SQL - mysql

I have a MySQL database populated with power consumption over 20 years.
I want to query the average of the power consumption over every month, from a given month.
For example with this database,
date power_consumption
2014/03/30 30
2014/04/30 40
2014/05/30 50
2014/06/30 20
The result would be, from 2014/04
month average_so_far_from_april_2014
2014/04 40.0
2014/05 45.0
2014/06 36.667
If I cannot achieve this in one query, what query should I go for to retrieve the most useful data for this task? (My naive approach is to query the whole table out and calculate the average in my application.)

Join the table of sub query against the consumption table which gets the unique months against the table of consumption, with a join condition that the year / month is less than or equal to the one from the sub query and use the AVG aggregate function on the power consumption from the table grouped by the year / month
Something like this:-
SELECT consumption_month,
AVG(b.power_consumption)
FROM
(
SELECT DISTINCT DATE_FORMAT(`date`, '%Y%m') AS consumption_month FROM consumption_table a
) a
INNER JOIN consumption_table b
ON consumption_month >= DATE_FORMAT(b.`date`, '%Y%m')
WHERE b.`date` >= '2014/04/01'
GROUP BY consumption_month
SQL fiddle:-
http://www.sqlfiddle.com/#!2/16588/2
If you only had one record per month you could simplify it more by just doing a join of the table against itself without the need for the sub query.

GROUP BY is for this kind of problems. The average is calculated for each distinct value of the expression the the GROUP BY clause.
SELECT DATE_FORMAT(date, '%Y/%m'), AVG(power_consumption)
FROM table_name
WHERE date > ...
GROUP BY DATE_FORMAT(date, '%y/%m')
ORDER BY DATE_FORMAT(date, '%y/%m')
You get the average for each month, DATE_FORMAT(date, '%y/%m') is year and month in format YYYY/MM

Related

I need the way to aggregate based on column value using MySQL

I am learning Charts in Laravel, i need to draw Line graph for daily Student Attendance for those students come late or on time. I tried to write MYSQL query but it doesn't work
I tried subquery on same table to get data for daily students and i also need 7 dates only not full date, like date is stored in db as 09/08/2019 but i need it as 08 as date.
SELECT Date, COUNT(*) AS TimeStudent
FROM attendance WHERE `Attendance`='OnTime' AND (SELECT COUNT(*) AS
LateStudent FROM attendance
WHERE `Attendance`='Late'
GROUP BY `Date`
ORDER BY LateStudent DESC)
GROUP BY `Date`
ORDER BY TimeStudent DESC
but i got
[Err] 1241 - Operand should contain 1 column(s)
, because i can't use to fetch Date again in subquery while use it after where clause. Any one help me plz.
Here is a way to aggregate based on column value.
This query will give you count of on time and late student for a particular date.
SELECT
`Date`,
DATE_FORMAT(`Date`, '%d') AS Month_Date, -- You can modify it as per your requirement
SUM(IF(`Attendance` = 'OnTime', 1, 0)) AS OnTime_Count,
SUM(IF(`Attendance` = 'Late', 1, 0)) AS Late_Count
FROM attendance
WHERE `Date` >= CURRENT_DATE - INTERVAL 7 DAY
GROUP BY `Date`;

related to query using SQL

In oracle sql, how to get the count of newly added customers only for the month of april and may and make sure they werent there in the previous months
SELECT CUSTOMER ID , COUNT(*)
FROM TABLE
WHERE DATE BETWEEN '1-APR-2018' AND '31-MAY-2018' AND ...
If we give max (date) and min(date), we can compare the greater date to check if this customer is new , correct?
expected output is month count
april ---
may ---
should show the exact count how many new customers joined in these two months
One approach is to use aggregation:
select customer_id, min(date) as min_date
from t
group by customer_id
having min(date) >= date '2018-04-01 and
min(date) < date '2018-06-01';
This gets the list of customers (which your query seems to be doing). To get the count, just use count(*) and make this a subquery.

how to write a Query in Mysql

I have 2 tables.ms_expese and track_expense.Using this table generate a fact table
I want the expense_name in ms_expense,expense_amount from track_expense.
I want to get the sum of expense_amount for a particular expense_name based on date.The date in the order of 1,2...12 as month id
SELECT DATE_Format(a.date,'%b') as month_id,b.expense_name AS expense_type, sum(a.expense_amount) AS expense_amount FROM ms_expense b JOIN track_expense a on a.`expense_id`=b.`expense_id` group by DATE_Format(a.date,'%b')
how to put the month id in the order of 1,2,..12 and my date format y-m-d
I get the month in apr,aug and so on but i need jan as 1,feb as 2
I have 25 expenses(expense name).In this query i got the total expense amount of first expense only.I want the total expense of all expenses in every month
CREATE TABLE fact AS
(<your select query>)
Your select query can be in the following form
SELECT MONTH(date)as month_id,expense_name,sum(expense_amount)
FROM ms_expense JOIN track_expense using (expense_id)
group by expense_name,MONTH(date)

How can I optimize the query below which uses three levels of select statements?

How to optimize the below query:
I have two tables, 'calendar_table' and 'consumption', Here I use this query to calculate monthly consumption for each year.
The calendar table has day, month and year for years 2005 - 2009 and consumption table has billed consumption data for monthly bill cycle. This query will count the number of days for each bill and use that the find the consumption for each month.
SELECT id,
date_from as bill_start_date,
theYear as Year,
MONTHNAME(STR_TO_DATE(theMonth, '%m')) as month,
sum(DaysOnBill),
TotalDaysInTheMonth,
sum(perDayConsumption * DaysOnBill) as EstimatedConsumption
FROM
(
SELECT
id,
date_from,
theYear,
theMonth, # use theMonth for displaying the month as a number
COUNT(*) AS DaysOnBill,
TotalDaysInTheMonth,
perDayConsumption
FROM
(
SELECT
c.id,
c.date_from as date_from,
ct.dt,
y AS theYear,
month AS theMonth,
DAY(LAST_DAY(ct.dt)) as TotalDaysInTheMonth,
perDayConsumption
FROM
consumption AS c
INNER JOIN
calendar_table AS ct
ON ct.dt >= c.date_from
AND ct.dt<= c.date_to
) AS allDates
GROUP BY
id,
date_from,
theYear,
theMonth ) AS estimates
GROUP BY
id,
theYear,
theMonth;
It is taking around 1000 seconds to go through around 1 million records. Can something be done to make it faster?.
The query is a bit dubious pretending to do one grouping first and then building on that with another, which actually isn't the case.
First the bill gets joined with all its days. Then we group by bill plus month and year thus getting a monthly view on the data. This could be done in one pass, but the query is joining first and then using the result as a derived table which gets aggregated. At last the results are taken again and "another" group is built, which is actually the same as before (bill plus month and year) and some pseudo aggregations are done (e.g. sum(perDayConsumption * DaysOnBill) which is the same as perDayConsumption * DaysOnBill, as SUM sums one record only here).
This can simply written as:
SELECT
c.id,
c.date_from as bill_start_date,
ct.y AS Year,
MONTHNAME(STR_TO_DATE(ct.month, '%m')) as month,
COUNT(*) AS DaysOnBill,
DAY(LAST_DAY(ct.dt)) as TotalDaysInTheMonth,
SUM(c.perDayConsumption) as EstimatedConsumption
FROM consumption AS c
INNER JOIN calendar_table AS ct ON ct.dt BETWEEN c.date_from AND c.date_to
GROUP BY
c.id,
ct.y,
ct.month;
I don't know if this will be faster or if MySQL's optimizer doesn't see through your query itself and boils it down to this anyhow.

MySQL Aggregate function in other aggregate function

I'm having a table with posts. Like (id int, date datetime).
How can I select average posts per day count for each month with one sql request?
Thank you!
This should do it for you:
select month, avg(posts_per_day)
from (select day(date), month(date) as month, count(*) as posts_per_day
from posts group by 1,2) x
group by 1
Explanation: Because you are doing an aggregate on an aggregate, there is no getting around doing a query on a query:
The inner query calculates the number per day and captures the month.
The outer query averages this count , grouping by month.
You can get the number of posts per month like this:
SELECT COUNT(*) AS num_posts_per_month FROM table GROUP BY MONTH(date);
Now we need the number of days in a month:
SELECT COUNT(*) / DATEDIFF(MAKEDATE(YEAR(date), MONTH(date)) + INTERVAL 1 MONTH, MAKEDATE(YEAR(date), MONTH(date))) AS avg_over_month
FROM table GROUP BY MONTH(date);
This will get the average number of posts per day during the calendar month of the post. That is, averages during the current month will continue to rise until the end of the month. If you want real averages during the current month, you have to put in a conditional to get the true number of elapsed days.