Double group by SQL for day of week and hour/minute - mysql

Every minute I have a value in my database. Each record has a timestamp, and a value.
"30695","2015-09-06 18:10:09","693"
I want to get the average value for each minute for each day of the week, but I'm not sure how to build a query for this.
My thinking is that I first need to group all the records by DATEPART(weekday,[time]), which will put all my data into Sunday, Monday, Tuesday, etc. Next, I would group by minute, rounded. (So 2015/11/30 06:41:28 would get grouped with 2015/11/23 06:41:56 (both dates are Mondays)). Then, take the average of the value at each minute--7*24*60 = 10,080 total values.

There is no datepart() function in mysql, but you can use dayofweek() to get the day of the week and minute() to get the minute part:
select dayofweek(timestamp) as day, minute(timestamp) as minute, avg(value_field) as average
from table
group by dayofweek(timestamp), minute(timestamp);

Got it working, adapted from Shadow's answer, which wasn't quite right. Here's my query (MySQL 5.5)
SELECT weekday(timestamp) as day_of_week, avg(avg) as average, DATE_FORMAT(timestamp,'%H:%i') as hour_time
FROM audio
GOUP BY hour_time, day_of_week
ORDER BY `day_of_week` ASC

Related

Generating time series reports

I'm trying to work out how to create a solution that will allow me to query a table that has a timestamp, and in return get a time series data. The request consists of start/end date & time, granularity type (minute, hour, day, week, month and year) and granularity value. Having tried to use in a query something like
GROUP BY ROUND(UNIX_TIMESTAMP(created_at) DIV 60)
to get the results per one minute, or DIV 300 for every five minutes is fine. The problem lies further up for calculating months and years' seconds which will be inaccurate. I've stumbled upon the generate_series in PGSQL (MySQL alternative) and am stuck trying to tie them together. How do I calculate a count of rows, for example, for two days, on a 15 minute granularity? It's a complex question that I'll probably have to break down further.
I have already visited #1 and #2, but they are incomplete.
To me it seems that rounding will only be allowed to certain level and I'd have to restrict it (i.e .for 2 months period there cannot be hourly breakdown).
EDIT
It gave me the wrong impression - I would not have to calculate monthly figures based on seconds using the query like:
SELECT DATE_FORMAT(MIN(created_at),'%d/%m/%Y %H:%i:%s' as date,
COUNT(*) AS count FROM guests
GROUP BY ROUND(UNIX_TIMESTAMP(created_at) / 300)
It's only going to do grouping based on minimum value. But the question still stands - is the best approach really to go through the time period using granularity value and "slice" the data that way without loosing too much accuracy?
It seems that the only approach is to run sub-queries for a set of data (i.e. for a period of two months, generate 15 minute intervals timestamps, group the data into them and produce an aggregate) without dividing the original timestamp to produce the rounded approximation.
Let's say you have a gigantic table measure with two columns datestamp and temp.
Let's say you want to see the temperature every six minutes (10x per hour) for the last week. You can do this sort of thing. We'll get to defining trunc in a moment.
SELECT trunc(datestamp) datestamp, AVG(temp) temp
FROM measure
WHERE datestamp >= CURDATE() - INVERVAL 7 DAY
GROUP BY trunc(datestamp)
ORDER BY trunc(datestamp)
That works for any reasonable definition of trunc. In this case trunc(t) returns the beginning of the six-minute period in which t occurs. So, trunc('1942-12-07 08:45:17') gives 1942-12-07 08:42:00).
Here's a query that works for every six minute interval.
SELECT DATE_FORMAT(datestamp,'%Y-%m-%d %H:00') +
INTERVAL (MINUTE(datestamp) -
MINUTE(datestamp) MOD 6) datestamp,
AVG(temp) temp
FROM measure
WHERE datestamp >= CURDATE() - INVERVAL 7 DAY
GROUP BY DATE_FORMAT(datestamp,'%Y-%m-%d %H:00') +
INTERVAL (MINUTE(datestamp) -
MINUTE(datestamp) MOD 6)
ORDER BY 1
This uses inbuilt date arithmetic rather than unix timestamp arithmetic.
You can use a stored function to make this easier to read.
DELIMITER $$
DROP FUNCTION IF EXISTS TRUNC_N_MINUTES$$
CREATE
FUNCTION TRUNC_N_MINUTES(datestamp DATETIME, n INT)
RETURNS DATETIME DETERMINISTIC NO SQL
COMMENT 'truncate to N minute boundary. For example,
TRUNCATE_N_MINUTES(sometime, 15) gives the nearest
preceding quarter hour'
RETURN DATE_FORMAT(datestamp,'%Y-%m-%d %H:00') +
INTERVAL (MINUTE(datestamp) -
MINUTE(datestamp) MOD n) MINUTE$$
DELIMITER ;
Then your query will say
SELECT TRUNC_N_MINUTES(datestamp, 6) datestamp, AVG(temp) temp
FROM measure
WHERE datestamp >= CURDATE() - INVERVAL 7 DAY
GROUP BY TRUNC_N_MINUTES(datestamp, 6)
ORDER BY TRUNC_N_MINUTES(datestamp, 6)
If you want to summarize by 5, 10, 15, or minute boundaries (three items per hour) simply use that number in place of 6.
You'll need different trunc() functions for hours, etc.
The trunc() function for daily summaries is DATE(datestamp).
For monthly summaries it is LAST_DAY(datestamp). For example,
SELECT LAST_DAY(datestamp) month_ending, AVG(temp) temp
FROM measure
GROUP BY LAST_DAY(datestamp)
ORDER BY LAST_DAY(datestamp)
yields a month-by-month summary.

Show week beginning date with week()

I am using the week function like so:
SELECT
sub.id AS ARID,
WEEK(my.data) AS Week,
Is it possible to add the week beginning date that the week corresponds to?
Something like:
SELECT
sub.id AS ARID,
WEEK(my.data) AS Week,
MIN(my.data) AS WKBeginning
Using MIN in this way I remove a lot of data unintentionally - it should be divorced fromt he data even. Put another way, I'd like to display Week as a date rather than a number that is along the lines 'Monday-2014-09-04'
Is that possible?
My first answer turned out to be incorrect if the source table didn't include the full range of dates for the week (specifically the first day of the week).
The correct way is to use ADDDATE():
ADDDATE(my.data, INTERVAL 1-DAYOFWEEK(my.data) DAY) AS CorrectWKBeginning,
A correlated sub-query should does not work when the first day of the week is not included in the data:
SELECT
id,
WEEK(d) AS Week,
ADDDATE(d, INTERVAL 1-DAYOFWEEK(d) DAY) AS CorrectWKBeginning,
(SELECT MIN(d) FROM t WHERE WEEK(d) = WEEK(t2.d)) as IncorrectWKBeginning
FROM t t2
Sample SQL Fiddle
You might have to use week() with parameters to set the start day of the week and if it counts from 0-53 or 1-53, see the MySQL manual for reference.

Grabbing date records that are exactly a month or more away

I put together a query for determining whether the time difference between the current date and a record from a database is exactly a month or more apart. I am comparing now() to a created_at column, which is a timestamp.
EX:
6-12-2014,
7-12-2014
AND
5-12-2014,
7-12-2014
Should be considered to be a desirable results.
SELECT count(*) FROM `subscriptions` WHERE
DATE_ADD(CAST(created_at as DATE),INTERVAL TIMESTAMPDIFF(MONTH, created_at, now()) MONTH) = CAST(now() as DATE);
However the query appears to not return all desired results. It returns 2-28-2014 and 7-28-2014, however it does not pull up 6-28-2014. Is there a better way of doing this than the solution I came up with?
Are you looking to count dates that are on the same day of the month as the current date? If so, try the DAYOFMONTH function:
SELECT COUNT(*)
FROM subscriptions
WHERE DAYOFMONTH(created_at) = DAYOFMONTH(NOW())

Need daily count of whole month - SQL2008

I am trying to generate simple report which gives me Failed test count daily for whole month. Is there a way to write a sql query that does that? Rather then running queries by changing dates? This is what i am doing:
select count (*) from students where Message Like 'Failed Test.%'
and Timestamp > '01-01-2013' and Timestamp < '01-02-2013'
So above query gives me count 5. Then i change the date from 01-01 to 01-02 and get next days count and so on. But this seems like very time consuming thing. Just checking if there is a better way to do that.
This gives counts grouped by day for a month's date range:
select cast(timestamp as date) as [Date], count (*) as NumFailedTests
from students
where Message Like 'Failed Test.%'
and Timestamp between '2013-01-01' and '2013-01-31'
group by cast(timestamp as date)
order by cast(timestamp as date)
You can obviously widen the date range...
[Hopefully the 'timestamp' column is not really a timestamp...]

how to get week average from month in mysql?

i have day, month, year, value columns in one table, here i need to get every week average value in one month.
how to get that. please help me regarding this.
select avg(value) from table group by month
gives month average.
select avg(value) from table group by day
gives day average.
but how to get week average from month field.
You can't "get weeks from a month" as one is not a subset of the other.
The number of days (and hence weeks) in 1 month varies from month to month and only in non-leap years - and in February only - are there exactly 4 weeks in a month.
You should use the original date field and use a date function to limit/group the data by week.
get the week in mySQL with
WEEK(timestamp)
or
YEARWEEK(timestamp)
or
WEEKOFYEAR(NOW())
or
DATE_FORMAT($yourDate, \'%X %V\') as week
There might be a better way, but my first thought is to write a query that calculates the week of the year for each day and groups them.
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_weekofyear
Something like:
SELECT avg(value) FROM table GROUP BY WEEKOFYEAR(CONCAT(year, '-', month, '-', day))