I have a query:
SELECT
COUNT(id) as amount,
DATEDIFF(expire, buydate) as days
FROM `vw8mv_orders`
GROUP BY MONTH(expire)
The result is:
amount days
1 22
1 30
1 105
1 161
I'd like to see these results in a group (every 30 days). If days value is between 1 and 30 days, then put this in 30days group, if bet 31-60, put to 60days group, etc.
For example:
amount time
2 30 days
0 60 days
1 90 days
You will need to create a calculated column to group by. There are several approaches you could use for the calculation, but a good option might be integer division using the DIV operator:
SELECT
COUNT(id) as amount,
(((datediff(expire, buydate) DIV 30) + 1) * 30) as timegroup
FROM
table
GROUP BY timegroup;
The reason I like this approach, rather than using for example some fancy arithmetic with ROUND(), is that it's a little more clear what you're trying to do. datediff(expire, buydate) DIV 30 says, take the difference of these dates, and tell me "how many 30s" are in that number.
That's all you need for your grouping; the rest is there to make the column display the way you want it, as 30, 60, 90, ... instead of as 0, 1, 2, ....
Another option, if you're not comfortable with integer division, would be the CEILING function:
SELECT
COUNT(id) as amount,
30 * CEILING(datediff(expire, buydate) / 30) as timegroup
FROM
table
GROUP BY timegroup;
Mathematically speaking, CEILING(x / N) is equivalent to ((x DIV N) + 1), but it's a little less busy with CEILING().
You can do a subselect over the result returned from your query,below is the example query
SELECT COUNT(`amount`) as amount,
CONCAT(ROUND(`days` / 30) * 30, ' Days')
as `time`
FROM `t`
GROUP BY `time`
ORDER BY ROUND(`days` / 30)
Demo
For your query you can do so
SELECT COUNT(`amount`) as amount,
CONCAT(ROUND(`days` / 30) * 30, ' Days')
as `time`
FROM(
SELECT COUNT(id) as amount,
datediff(expire, buydate) as days
FROM `vw8mv_orders`
GROUP BY MONTH(expire)
) t
GROUP BY `time`
ORDER BY ROUND(`days` / 30)
Related
I've got a monitoring system that is collecting data every n seconds (n is approximately 10 but varies). I'd like to aggregate the collected data by 15 minute intervals. Is there a way to consolidate the timestamp values into 15 minute chunks to allow for grouping to work?
SELECT FLOOR(UNIX_TIMESTAMP(timestamp)/(15 * 60)) AS timekey
FROM table
GROUP BY timekey;
Try this , grouping of records of 15 minutes interval, you can change 15*60 to the interval in seconds you need
SELECT sec_to_time(time_to_sec(datefield)- time_to_sec(datefield)%(15*60)) as intervals from tablename
group by intervals
Adaptation of approach 1) below:
select Round(date_format(date, "%i") / (15*60)) AS interval
from table
group by interval
Adaptation of approach 3) below:
SELECT Round(Convert(substring(date_column, 14, 2), UNSIGNED) / (15*60)) AS interval /* e.g. 2009-01-04 12:20:00 */
FROM table
GROUP BY interval;
A few approaches I've found here:
1)
select date_format(date, "%W") AS `Day of the week`, sum(cost)
from daily_cost
group by `Day of the week`
order by date_format(date, "%w")
2)
select count(*) as 'count',
date_format(min(added_on), '%Y-%M-%d') as 'week commencing',
date_format(added_on, '%Y%u') as 'week'
from system
where added_on >= '2007-05-16'
group by week
order by 3 desc;
3)
SELECT substring(postdate, 1,10) AS dd, COUNT(id) FROM MyTable GROUP BY dd;
(Also here: http://www.bradino.com/mysql/dayparting-on-datetime-field-using-substring/)
EDIT: All the solutions will perform badly on a table with a large number of records.
I started with the answer given above by unutbu but didn't get what I needed and had to add a bit to it.
Select Created, from_unixtime(FLOOR(UNIX_TIMESTAMP(Created)/(15*60))*(15*60)) GroupTime,
COUNT(*) as Cnt
FROM issue i
GROUP BY GroupTime
This code divides by the 900 seconds in a 15 minute span then floors the value and multiplies it back up by 900, essentially rounding down to the nearest 15 minute increment.
Following query groups rows and creates timestamps at 15 min intervals.
Select concat( date(created_dt) , ' ', sec_to_time(time_to_sec(created_dt)- time_to_sec(created_dt)%(15*60) + (15*60)))as created_dt_new from table_name group by created_dt_new
E.g Timestamps
2016-11-09 13:16:29
2016-11-09 13:16:49
2016-11-09 13:17:06
2016-11-09 13:17:26
2016-11-09 13:18:24
2016-11-09 13:19:59
2016-11-09 13:21:17
Are grouped into 2016-11-09 13:30:00
sec_to_time(time_to_sec(created_dt)- time_to_sec(created_dt)%(15*60) + (15*60)))
Upper bounds time to nearest 15 min interval. e.g 12:10 -> 12:15
concat( date(created_dt) , ' ', sec_to_time(time_to_sec(created_dt)- time_to_sec(created_dt)%(15*60) + (15*60)))
Generates a timestamp taking the date from the timestamp field.
Unix timestamps: floor them to nearest 15 minute using one of the following:
timestamp div (15 * 60) * (15 * 60) -- div is integer division operator
timestamp - timestamp % (15 * 60)
Date time: assuming the datatype does not have fractional seconds, floor them to nearest 15 minute using:
date - INTERVAL EXTRACT(SECOND FROM date) SECOND - INTERVAL EXTRACT(MINUTE FROM date) % 15 MINUTE
DBFiddle
This worked for me
mysql> **SELECT FROM_UNIXTIME(UNIX_TIMESTAMP(NOW())- UNIX_TIMESTAMP(NOW())%(15*60));**
+---------------------------------------------------------------------+
| FROM_UNIXTIME(UNIX_TIMESTAMP(NOW())- UNIX_TIMESTAMP(NOW())%(15*60)) |
+---------------------------------------------------------------------+
| 2012-02-09 11:15:00 |
+---------------------------------------------------------------------+
1 row in set (0.00 sec)
THis Work for me
SELECT CONCAT (
YEAR(transactionDate)
,'-'
,MONTH(transactionDate)
,'-'
,DAYOFMONTH(transactionDate)
,' '
,HOUR(transactionDate)
,':'
,((floor((MINUTE(transactionDate) / 15)) + 1) * 15) - 1
,':59'
) AS tmp1
,count(*)
FROM tablename
GROUP BY tmp1 limit 20;
Change "15" to whatever interval you want.
select count(*),
CONCAT(HOUR(col_date),":",(MINUTE(create_date) div 15)*15) as date
from tablename
GROUP BY date
ORDER BY col_date ASC;
I was not satisfied by GROUP BY.
SELECT datetime
FROM table
WHERE MOD(MINUTE(TIME(datetime)),15) = 0 AND SECOND(TIME(datetime)) = 0;
I have a table which includes a time stamp and river flow. Some days I have multiple records, yet other days I have no records.
How can I calculate the average flow and total flow between two dates?
Assuming a linear value between two points is acceptable. Maybe some sort of weighted average. If there is some least squares regression algorithm or something similar which provides more accurate results, that would also be great.
EDIT. For a given day I have the following fictional data for illustration purposes. If possible, I would like to do better than assuming an average of 146 as the flows are high for longer duration's, and the real average is likely over 200.
10/12/15 12:00 AM 100
10/12/15 12:01 AM 102
10/12/15 12:02 AM 104
10/12/15 12:03 AM 106
10/12/15 12:04 AM 200
10/12/15 10:00 PM 204
10/12/15 11:00 PM 208
Average 146
You need to do a weighted average. For this, you need the next time stamp:
select rf.*,
(select rf2.timestamp
from riverflow rf2
where rf2.timestamp > rf.timestamp
order by rf.timestamp asc
limit 1
) as nextTimestamp
from riverflow rf;
Next, comes the weighted average. I have no idea how you want to handle the issue that the period of measurement may not align with given days. Instead, we'll just take values and report on the beginning and ending observation times:
select min(timestamp) as start, max(timestamp) as end,
(sum(riverflow * timestampdiff(second, timestamp, nexttimestamp) / (24*60*60)) /
(timestampdiff(second, min(timestamp), max(timestamp)) / (24*60*60)
) as avgRiverflow
from (select rf.*,
(select rf2.timestamp
from riverflow rf2
where rf2.timestamp > rf.timestamp
order by rf2.timestamp asc
limit 1
) as nextTimestamp
from riverflow rf
where timestamp >= $date1 and timestamp < $date2
) t;
Something like this should be generally the right direction:
SELECT AVG(dayflowRate) AS avgFlowRate, SUM(dayFlow) AS totalFlow
FROM (
SELECT DATE(theTS) AS theDate, AVG(flowRate) AS dayFlowRate
, AVG(flowRate) * (24*60*60) AS dayFlow
FROM theTable
WHERE theTS BETWEEN [beginTS] AND [endTS]
GROUP BY theDate
) AS dayQ
;
However, it assumes full days with that 24 * 60 * 60 multiplier (expanded only for clarity by the way). If you need greater precision, you will want to look into MIN and MAX aggregations, and TIME_TO_SEC function.
I think this (below) might be a little more accurate:
SELECT AVG(dayflowRate) AS avgFlowRate, SUM(dayFlow) AS totalFlow
FROM (
SELECT DATE(theTS) AS theDate, AVG(flowRate) AS dayFlowRate
, AVG(flowRate)
* ( TIME_TO_SEC(LEAST(MAX(theTS), [endTS])
- TIME_TO_SEC(GREATEST(MIN(theTS), [beginTS]))
)
AS dayFlow
FROM theTable
WHERE theTS BETWEEN [beginTS] AND [endTS]
GROUP BY theDate
) AS dayQ
;
Edit: or might not, if that day's measurements are at 11am and 1pm, it's dayFlow will only actually be for two hours of flow, even if it is in the middle of multiple days.
This should be best:
SELECT AVG(dayflowRate) AS avgFlowRate, SUM(dayFlow) AS totalFlow
FROM (
SELECT DATE(theTS) AS theDate, AVG(flowRate) AS dayFlowRate
, AVG(flowRate)
* ( IF(DATE(theTS)=DATE([endTS]), TIME_TO_SEC([endTS]), (24*60*60))
- IF(DATE(theTS)=DATE([beginTS]), TIME_TO_SEC([beginTS]), 0)
)
AS dayFlow
FROM theTable
WHERE theTS BETWEEN [beginTS] AND [endTS]
GROUP BY theDate
) AS dayQ
;
I have some troubles in grouping some datasets. Here is my query and query result.
SELECT COUNT(*),salarie_id, created_at
FROM enregistrement
GROUP BY salarie_id, created_at
My point is to group similar created_at rows, in an interval of +/- 3 sec. I didn't manage it, even in using INTERVAL, in such criterias as HAVING
, WHERE ... BETWEEN.
How can I group these rows to get, for example 36 (33+3) in my count result, as shown in the image ?
Didn't found any suitable solution... Let me know if you want some additional information.
UPDATE 1 : Looks like #fancypants solution is on the right way.
SELECT COUNT(*),salarie_id, DATE_FORMAT(created_at, CONCAT('%Y-%m-%d %H:%i:', LEFT(RIGHT(created_at, 2), 1), RIGHT(created_at, 1) % 3)) AS 'date'
FROM enregistrement
GROUP BY salarie_id, DATE_FORMAT(created_at, CONCAT('%Y-%m-%d %H:%i:', LEFT(RIGHT(created_at, 2), 1), RIGHT(created_at, 1) % 3))
SELECT COUNT(*),salarie_id, created_at
FROM enregistrement
GROUP BY salarie_id,
DATE_FORMAT(created_at, CONCAT('%Y-%m-%d %H:%i:', LEFT(RIGHT(created_at, 2), 1),
CASE WHEN RIGHT(created_at, 1) BETWEEN 1 AND 4 THEN 0
WHEN RIGHT(created_at, 1) BETWEEN 5 AND 7 THEN 1
ELSE 2 END
));
You can use DATE_FORMAT() to bring your datetime/timestamp column in certain shapes. In your case we specify the format with
CONCAT('%Y-%m-%d %H:%i:', LEFT(RIGHT(created_at, 2), 1), RIGHT(created_at, 1) % 3))
That is, we take the year, the month, the day, the hour, the minute. Then we concatenate the date format string with the power of ten of seconds.
For example you have this seconds:
21
33
36
40
Then we take 2, 3, 3 and 4 with LEFT(RIGHT(created_at, 2), 1). The 1, 3, 6 and 0 we get with RIGHT(created_at, 1). Then you just have to put them into self-defined groups with CASE WHEN.
You need to create the column in your select, and then you can group by this column. Here is an example:
SELECT
COUNT(*),
salarie_id,
IF(created_at BETWEEN "2014-10-01 00:00:00" AND "2014-10-01 00:00:00", "2014-10-01 00:00:00"
ELSEIF(created_at BETWEEN "2015-02-18 11:05:43" AND "2015-02-18 11:05:46", "2015-02-18 11:05:43",
ELSEIF( ...
))) as buckets
FROM enregistrement
GROUP BY salarie_id, bucket
I am not sure how practical this may be in your case though, since you probably do not know the intervals beforehand.
An easy way to get through in your example would be to simply group by date and time without the seconds:
SELECT
DATE_FORMAT(created_at,"%Y-%m-%d %H:%i") as short_date
.....
GROUP BY salarie_id, short_date
Suppose you have a table of the form:
create table user_activity (
user_id int not null,
activity_date timestamp not null,
...);
It's easy enough to select the number of unique user_id's in the past 30 days.
select count(distinct user_id) from user_activity where activity_date > now() - interval 30 day;
But how can you select the number of unique user_ids in the prior 30 days for each of the past 30 days? E.g. uniques for 0-30 days ago, 1-31 days ago, 2-32 days ago and so on to 30-60 days ago.
The database engine is mysql if it matters
You could try using a sub query:
SELECT DISTINCT `activity_date` as `day`, (
SELECT count(DISTINCT `user_id`) FROM `user_activity` WHERE `activity_date` = `day`
) as `num_uniques`
FROM `user_activity`
WHERE `activity_date` > NOW() - INTERVAL 30 day;
This should give you the number of unique users for each day. However, I haven't tested this since I don't have the DB to work with.
I haven't tried this in MySQL, but hopefully the syntax is right. If not, maybe it will point you in the right direction. First, I often employ a Numbers table. It can be a physical table simply made up of numbers or it can be a generated/virtual/temporary table.
SELECT
N.number,
COUNT(DISTINCT UA.user_id)
FROM
Numbers N
INNER JOIN User_Activity UA ON
UA.activity_date > NOW() - INTERVAL 30 + N.number DAY AND
UA.activity_date <= NOW() - INTERVAL N.number DAY
WHERE
N.number BETWEEN 0 AND 30
GROUP BY
N.number
I'm not familiar with the whole INTERVAL syntax, so if I got that wrong, please let me know and I'll try to correct it.
If you get the days number for todays date and mod it by 30 you get the offset of the current day. Then you add that to each number for a date and divide the result by 30, this gives you the group of days. Then group your results by this number. So in code something like this:
select count(distinct user_id), (to_days(activity_date)+(to_days(now()) % 30)) / 30 as period
from user_activity
group by (to_days(activity_date)+(to_days(now()) % 30)) / 30
I will leave calculating the reverse numbering of period up to you (hint: take the period number for the current date as "max" and subtract period above and add 1.)
I've got a monitoring system that is collecting data every n seconds (n is approximately 10 but varies). I'd like to aggregate the collected data by 15 minute intervals. Is there a way to consolidate the timestamp values into 15 minute chunks to allow for grouping to work?
SELECT FLOOR(UNIX_TIMESTAMP(timestamp)/(15 * 60)) AS timekey
FROM table
GROUP BY timekey;
Try this , grouping of records of 15 minutes interval, you can change 15*60 to the interval in seconds you need
SELECT sec_to_time(time_to_sec(datefield)- time_to_sec(datefield)%(15*60)) as intervals from tablename
group by intervals
Adaptation of approach 1) below:
select Round(date_format(date, "%i") / (15*60)) AS interval
from table
group by interval
Adaptation of approach 3) below:
SELECT Round(Convert(substring(date_column, 14, 2), UNSIGNED) / (15*60)) AS interval /* e.g. 2009-01-04 12:20:00 */
FROM table
GROUP BY interval;
A few approaches I've found here:
1)
select date_format(date, "%W") AS `Day of the week`, sum(cost)
from daily_cost
group by `Day of the week`
order by date_format(date, "%w")
2)
select count(*) as 'count',
date_format(min(added_on), '%Y-%M-%d') as 'week commencing',
date_format(added_on, '%Y%u') as 'week'
from system
where added_on >= '2007-05-16'
group by week
order by 3 desc;
3)
SELECT substring(postdate, 1,10) AS dd, COUNT(id) FROM MyTable GROUP BY dd;
(Also here: http://www.bradino.com/mysql/dayparting-on-datetime-field-using-substring/)
EDIT: All the solutions will perform badly on a table with a large number of records.
I started with the answer given above by unutbu but didn't get what I needed and had to add a bit to it.
Select Created, from_unixtime(FLOOR(UNIX_TIMESTAMP(Created)/(15*60))*(15*60)) GroupTime,
COUNT(*) as Cnt
FROM issue i
GROUP BY GroupTime
This code divides by the 900 seconds in a 15 minute span then floors the value and multiplies it back up by 900, essentially rounding down to the nearest 15 minute increment.
Following query groups rows and creates timestamps at 15 min intervals.
Select concat( date(created_dt) , ' ', sec_to_time(time_to_sec(created_dt)- time_to_sec(created_dt)%(15*60) + (15*60)))as created_dt_new from table_name group by created_dt_new
E.g Timestamps
2016-11-09 13:16:29
2016-11-09 13:16:49
2016-11-09 13:17:06
2016-11-09 13:17:26
2016-11-09 13:18:24
2016-11-09 13:19:59
2016-11-09 13:21:17
Are grouped into 2016-11-09 13:30:00
sec_to_time(time_to_sec(created_dt)- time_to_sec(created_dt)%(15*60) + (15*60)))
Upper bounds time to nearest 15 min interval. e.g 12:10 -> 12:15
concat( date(created_dt) , ' ', sec_to_time(time_to_sec(created_dt)- time_to_sec(created_dt)%(15*60) + (15*60)))
Generates a timestamp taking the date from the timestamp field.
Unix timestamps: floor them to nearest 15 minute using one of the following:
timestamp div (15 * 60) * (15 * 60) -- div is integer division operator
timestamp - timestamp % (15 * 60)
Date time: assuming the datatype does not have fractional seconds, floor them to nearest 15 minute using:
date - INTERVAL EXTRACT(SECOND FROM date) SECOND - INTERVAL EXTRACT(MINUTE FROM date) % 15 MINUTE
DBFiddle
This worked for me
mysql> **SELECT FROM_UNIXTIME(UNIX_TIMESTAMP(NOW())- UNIX_TIMESTAMP(NOW())%(15*60));**
+---------------------------------------------------------------------+
| FROM_UNIXTIME(UNIX_TIMESTAMP(NOW())- UNIX_TIMESTAMP(NOW())%(15*60)) |
+---------------------------------------------------------------------+
| 2012-02-09 11:15:00 |
+---------------------------------------------------------------------+
1 row in set (0.00 sec)
THis Work for me
SELECT CONCAT (
YEAR(transactionDate)
,'-'
,MONTH(transactionDate)
,'-'
,DAYOFMONTH(transactionDate)
,' '
,HOUR(transactionDate)
,':'
,((floor((MINUTE(transactionDate) / 15)) + 1) * 15) - 1
,':59'
) AS tmp1
,count(*)
FROM tablename
GROUP BY tmp1 limit 20;
Change "15" to whatever interval you want.
select count(*),
CONCAT(HOUR(col_date),":",(MINUTE(create_date) div 15)*15) as date
from tablename
GROUP BY date
ORDER BY col_date ASC;
I was not satisfied by GROUP BY.
SELECT datetime
FROM table
WHERE MOD(MINUTE(TIME(datetime)),15) = 0 AND SECOND(TIME(datetime)) = 0;