Mysql - Get data of different time interval in same query - mysql

My below query is giving me result of top videos played in past 2 hours, but my requirement is to also get top videos of past 4 hours to past 2 hours, for example if by this query I am getting data from 01:00 PM to 03:00 PM, I also want data from 09:00 AM to 01:00 PM. Can I do this in one query and in efficient way.
Query:
select SQL_CACHE channel,SUBSTRING_INDEX(GROUP_CONCAT(video_id ORDER BY plays DESC),',', 40) AS video_ids,now() as datetime from
(SELECT channel,video_id,count(video_id) as plays FROM `tbl`
WHERE `datetime_col` > DATE_SUB( now(), INTERVAL 2 HOUR )
and channel != 0
and cat_id != 8
group by channel,video_id
order by channel,plays DESC)x
group by channel;
Thanks in advance.

You can use the same query with UNION to group the results. Also, you can use BETWEEN to define the intervals, e.g.
select SQL_CACHE channel,SUBSTRING_INDEX(GROUP_CONCAT(video_id ORDER BY plays DESC),',', 40) AS video_ids,now() as datetime from
(SELECT channel,video_id,count(video_id) as plays FROM `tbl`
WHERE `datetime_col` BETWEEN DATE_SUB( now(), INTERVAL 2 HOUR ) AND NOW()
and channel != 0
and cat_id != 8
group by channel,video_id
order by channel,plays DESC)x
group by channel
UNION
select SQL_CACHE channel,SUBSTRING_INDEX(GROUP_CONCAT(video_id ORDER BY plays DESC),',', 40) AS video_ids,DATE_SUB( now(), INTERVAL 2 HOUR ) as datetime from
(SELECT channel,video_id,count(video_id) as plays FROM `tbl`
WHERE `datetime_col` BETWEEN DATE_SUB( now(), INTERVAL 4 HOUR ) AND DATE_SUB( now(), INTERVAL 2 HOUR )
and channel != 0
and cat_id != 8
group by channel,video_id
order by channel,plays DESC)x
group by channel;

Related

Count Number of a Specific Day(s) Between Two Dates

I have a single line in MySQL table: volunteers
user_id | start_date | end_date
11122 | 2017-04-20 | 2018-02-17
How can I find how many times the 3rd day or 24th day of a month appears? (i.e. 2017-05-03, 2017-06-03, 2017-12-24, 2018-01-24) I'm trying to get to the following count:
Sample Output:
user_id | number_of_third_day | number_of_twenty_fourth_day
11122 | 10 | 10
I look at the documentation online to see if there is a way I can say (pseudo):
SELECT
day, COUNT(*)
FROM volunteers
WHERE day(between(start_date, end_date)) in (3,24)
I tried to create a calendar table to no avail, but I would try to get the days, GROUP BY day, and COUNT(*) times that day appears in the range
WITH calendar AS (
SELECT start_date AS date
FROM volunteers
UNION ALL
SELECT DATE_ADD(start_date, INTERVAL 1 DAY) as date
FROM volunteers
WHERE DATE_ADD(start_date, INTERVAL 1 DAY) <= end_date
)
SELECT date FROM calendar;
Thanks for any help!
This one is more optimized since I generate date range by months not days as other questions, so its faster
WITH RECURSIVE cte AS
(
SELECT user_id, DATE_FORMAT(start_date, '%Y-%m-03') as third_day,
DATE_FORMAT(start_date, '%Y-%m-24') as twenty_fourth_day,
start_date, end_date
FROM table1
UNION ALL
SELECT user_id,
DATE_FORMAT(third_day + INTERVAL 1 MONTH, '%Y-%m-03') as third_day,
DATE_FORMAT(twenty_fourth_day + INTERVAL 1 MONTH, '%Y-%m-24') as twenty_fourth_day,
start_date, end_date
FROM cte
WHERE third_day + INTERVAL 1 MONTH <= end_date
)
SELECT user_id,
SUM(CASE WHEN third_day BETWEEN start_date AND end_date THEN 1 ELSE 0 END) AS number_of_third_day,
SUM(CASE WHEN twenty_fourth_day BETWEEN start_date AND end_date THEN 1 ELSE 0 END) AS number_of_twenty_fourth_day
FROM cte
GROUP BY user_id;
Demo here
A dynamic approach is.
but creating the dateranges, takes a lot of time, so you should have a date table to get the dates
CREATE TABLE table1
(`user_id` int, `start_date` varchar(10), `end_date` varchar(10))
;
INSERT INTO table1
(`user_id`, `start_date`, `end_date`)
VALUES
(11122, '2017-04-20', '2018-02-17')
,(11123, '2019-04-20', '2020-02-17')
;
Records: 2 Duplicates: 0 Warnings: 0
WITH RECURSIVE cte AS (
SELECT
user_id,
`start_date` as date_run ,
`end_date`
FROM table1
UNION ALL
SELECT
user_id,
DATE_ADD(cte.date_run, INTERVAL 1 DAY),
end_date
FROM cte
WHERE DATE_ADD(date_run, INTERVAL 1 DAY) <= end_date
)SELECT user_id,
SUM(DAYOFMONTH(date_run) = 3) as day_3th,
SUM(DAYOFMONTH(date_run) = 24) as day_24th
FROM cte
GROUP BY user_id
user_id
day_3th
day_24th
11122
10
10
11123
10
10
fiddle
In last MySQL version you can use recursion:
-- get list of all dates in interval
with recursive dates(d) as (
select '2017-04-20'
union all
select date_add(d, interval 1 day) from dates where d < '2018-02-17'
) select
-- calculate
sum(day(d) = 10) days_10,
sum(day(d) = 24) days_24
from dates
-- filter 10 & 24 days
where day(d) = 10 or day(d) = 24;
https://sqlize.online/sql/mysql80/c00eb7de69d011a85502fa538d64d22c/
As long as you are looking for days that occur in every month (so not the 29th or beyond), this is just straightforward math. The number of whole calendar months between two dates (exclusive) is:
timestampdiff(month,start_date,end_date) - (day(start_date) <= day(end_date))
Then add one if the start month includes the target day and one if the end month includes it:
timestampdiff(month,start_date,end_date) - (day(start_date) <= day(end_date))
+ (day(start_date) <= 3) + (day(end_date) >= 3)

Elapsed Time Between Two Dates for specified time range

I have a MYSQL table with a TIMESTAMP column 'Start' and a TIMESTAMP column 'End'. I want to return the number of minutes between the start and the end (End is always after than Start). Usually I'd just use 'TIMESTAMPDIFF()' but this time I need to get the minutes from 9am until 22pm, of each day in that date range.
If a row has a Start '2017-01-01 07:15:00' and an End of '2017-01-02 11:30:00' - the elapsed time should be 15.5 hours (930 minutes).
I'm having trouble coming up with a decent way of doing this and my searching online hasn't found quite what I'm looking for. Can someone help me along?
Edit:
CREATE TABLE date_ranges (
Start TIMESTAMP,
End TIMESTAMP
);
INSERT INTO date_ranges VALUES('2017-01-01 07:15:00','2017-01-02 11:30:00');
I came up with this:
SELECT Start, End, TIMESTAMPDIFF(MINUTE, Start, End) AS MinutesElapsed
FROM date_ranges;
I'm missing the part where the time in minutes is calculated only in the specified time range (9am until 22pm). Any ideas?
Here you go:
SELECT t1, t2, (TIMESTAMPDIFF(MINUTE, t1, t2) - TIMESTAMPDIFF(DAY, t1, t2)*660) FROM
(SELECT CASE WHEN t1 < STR_TO_DATE(concat(date_format(t1, '%Y-%m-%d'), ' 09:00:00'), '%Y-%m-%d %h:%i:%s')
THEN STR_TO_DATE(concat(date_format(t1, '%Y-%m-%d'), ' 09:00:00'), '%Y-%m-%d %h:%i:%s')
ELSE t1
END AS t1 FROM test) test1,
(SELECT CASE WHEN t2 > STR_TO_DATE(concat(date_format(t2, '%Y-%m-%d'), ' 22:00:00'), '%Y-%m-%d %h:%i:%s')
THEN STR_TO_DATE(concat(date_format(t2, '%Y-%m-%d'), ' 22:00:00'), '%Y-%m-%d %h:%i:%s')
ELSE t2
END AS t2 FROM test) test2;
660 = number of minutes between 22:00 and 09:00 (11 hours)
Here's the SQL Fiddle.
It's not very concise, but this should give you the results you want:
select started_at,ended_at,
(case
when date(ended_at) = date(started_at)
then
timestampdiff(
minute,
greatest(started_at,concat(date(started_at),' 09:00:00')),
least(ended_at,concat(date(ended_at),' 22:00:00'))
)
else
timestampdiff(
minute,
least(greatest(started_at,concat(date(started_at),' 09:00:00')),concat(date(started_at),' 22:00:00')),
concat(date(started_at),' 22:00:00')
)
+
timestampdiff(
minute,
concat(date(ended_at),' 09:00:00'),
greatest(least(ended_at,concat(date(ended_at),' 22:00:00')),concat(date(ended_at),' 09:00:00'))
)
+ ((datediff(ended_at,started_at)-1)*780)
end) as total_minutes
from your_table;
--Generating all dates in 2017.
CREATE TABLE CALENDAR AS --Use a different table name if CALENDAR already exists
SELECT '2017-12-31 09:00:00' - INTERVAL c.number DAY AS start_datetime,'2017-12-31 22:00:00' - INTERVAL c.number DAY AS end_datetime
FROM (SELECT singles + tens + hundreds number FROM
(SELECT 0 singles
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) singles JOIN
(SELECT 0 tens
UNION ALL SELECT 10 UNION ALL SELECT 20 UNION ALL SELECT 30
UNION ALL SELECT 40 UNION ALL SELECT 50 UNION ALL SELECT 60
UNION ALL SELECT 70 UNION ALL SELECT 80 UNION ALL SELECT 90
) tens JOIN
(SELECT 0 hundreds
UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
UNION ALL SELECT 400 UNION ALL SELECT 500 UNION ALL SELECT 600
UNION ALL SELECT 700 UNION ALL SELECT 800 UNION ALL SELECT 900
) hundreds
ORDER BY number DESC) c
WHERE c.number BETWEEN 0 and 364
;
--End of table creation
--Actual query begins here
SELECT D.`START`,
D.`END`,
SUM(TIMESTAMPDIFF(MINUTE,GREATEST(D.`START`,C.START_DATETIME), LEAST(D.`END`,C.END_DATETIME))) AS TOTAL_TIME
FROM CALENDAR C
LEFT JOIN DATE_RANGES D ON DATE(C.START_DATETIME) >= DATE(D.`START`)
AND DATE(C.START_DATETIME) <= DATE(D.`END`)
WHERE D.`START` IS NOT NULL
AND D.`END` IS NOT NULL
GROUP BY D.`START`,
D.`END`
;
Construct a calendar table with a dates for a specified number of years. Each date having a start time of 09:00 and an end time of 22:00.
Left join on this table to get one row per date from the date ranges table.
Sum up the differences each day to get the total time worked.
Sample Demo
Day 1 Day 2 Day 3
|--********--|--********--|--********--|
|__________________________|
The question, IMHO is to know how many minutes the first day, and how many minutes the last day, the intermediate days have 780 minutes.
I've used a subquery just to help in the intermediate calculations.
select
if(hour(t1) < 9, date(t1) + interval 9 hour , t1) as tIni1,
date(t1) + interval 22 hour as tFin1,
date(t2) + interval 9 hour as tIni2,
if(hour(t2) > 22, date(t2) + interval 22 hour, t2) as tFin2,
TIMESTAMPDIFF(day, date(t1), date(t2)) numDays
from
tdt
tIni1 and tFin1 is the period of the first day, and tIni2, tFin2 the period of the last day, obviously first and last day can be the same.
Then calculate minutes of first day + minutes of second day + 780 minutes for every intermediate day.
select numDays, tIni1, tFin1, tIni2, tFin2,
if (numDays = 0,
TIMESTAMPDIFF(minute, tIni1, tFin2),
TIMESTAMPDIFF(minute, tIni1, tFin1)
+ TIMESTAMPDIFF(minute, tIni2, tFin2)
+ (numDays - 1) * 780
) as Minutes
from (
select
if(hour(t1) < 9, date(t1) + interval 9 hour , t1) as tIni1,
date(t1) + interval 22 hour as tFin1,
date(t2) + interval 9 hour as tIni2,
if(hour(t2) > 22, date(t2) + interval 22 hour, t2) as tFin2,
TIMESTAMPDIFF(day, date(t1), date(t2)) numDays
from
tdt
) ti
;
Try it here: http://rextester.com/GDHAB78973

Get records of last hour, last 2 hours.....last n hours

I need to get the records of the last 24 hours but not group by hour like this:
SELECT HOUR(CompDate) AS hour, COUNT(1) AS action
FROM mytable
WHERE ((CompDate >= DATE_SUB(NOW(), INTERVAL 24 HOUR))
GROUP BY `hour`;
The above query will tell me that: hour 22 --> 6 actions, hour 21 --> 9 actions.
What I want to have is: 1 hour ago --> 5 actions, 2 hours ago --> 3 actions, etc...
I need to get the sum of actions from hour ago, 2 hours ago.....n hours ago
Any help would be appreciated
You can do It in following:
CREATE TABLE Test
(
`Id` INT,
`DateTimes` DATETIME
);
INSERT INTO Test(Id, DateTimes) VALUES (1, '2015-06-09 10:12:12'), (2, '2015-06-09 10:13:12'), (3, '2015-06-09 09:12:12'), (4, '2015-06-09 09:15:12'), (5, '2015-06-09 08:15:10')
SELECT TIMESTAMPDIFF(HOUR,NOW(),DateTimes) * -1 AS Hours,
COUNT(*) AS Action
FROM Test
GROUP BY TIMESTAMPDIFF(HOUR,NOW(),DateTimes)
OUTPUT:
Hour Action
24 1
23 2
22 2
SQL FIDDLE
Use Order By and Group By
SELECT HOUR(CompDate) AS hour, COUNT(1) AS action
FROM mytable
WHERE ((CompDate >= DATE_SUB(NOW(), INTERVAL 24 HOUR))
GROUP BY HOUR(CompDate)
ORDER BY HOUR(CompDate);
Use Concat if you want to add hours ago from query itself
I guess the only change you need to do is to convert your HOUR to the difference between NOW and the CompDate:
SELECT HOUR( timediff( NOW( ) , CompDate) ) AS HOUR , COUNT( 1 ) AS action
FROM mytable
WHERE CompDate > DATE_SUB( NOW( ) , INTERVAL 24 HOUR )
GROUP BY HOUR
ORDER BY HOUR

mysql get sum of hours / minutes / seconds

I have a table with: userid and timestamp each time a user opens a page a new field is inserted.
I am trying to get the total amount of hours / minutes / days / weeks that appear in a 1 month interval for multiple users.
I have tried a bunch of different queries but each have ended up terribly inefficient.
Ideally I'd like to end up with something like:
userid | minutes | hours | days | weeks
1 10080 168 7 1
2 1440 24 1 0
Hopefully someone can shed some light on how to do this.
Below is a query that I tried:
SELECT
w.time AS `week`,
d.time AS `day`,
h.time AS `hour`,
m.time AS `minutes`
FROM (
SELECT
SUM( t.time ) AS `time`
FROM (
SELECT
COUNT( DISTINCT WEEK( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "1"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY MONTH( `timestamp` )
) t
) w,
(
SELECT
SUM( t.time ) AS `time`
FROM (
SELECT
COUNT( DISTINCT DAY( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "52"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY MONTH( `timestamp` )
) t
) d,
(
SELECT
SUM( t.timestamp ) AS `time`
FROM (
SELECT
COUNT( DISTINCT HOUR( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "1"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY DAY( `timestamp` )
) t
) h,
(
SELECT
SUM( t.timestamp ) AS `time`
FROM (
SELECT
COUNT( DISTINCT MINUTE( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "1"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY HOUR( `timestamp` )
) t
) m
It seems awfully excessive for this task, maybe someone has something better?
It's not clear to me what you want to "total".
If you want to determine whether a user had a "hit" (or whatever transaction it is you are storing in the table) at any given minute within the month), and then you want to count the number of "minute periods" within a month that a user had a hit:
SELECT t.userid
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%Y-%m-%d %H:%i')) AS minutes
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%Y-%m-%d %H' )) AS hours
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%Y-%m-%d' )) AS days
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%X-%V' )) AS weeks
FROM mytable t
WHERE t.timestamp >= '2012-06-01'
AND t.timestamp < '2012=07-01'
GROUP BY t.userid
What this is doing is taking each timestamp, and putting it into a "bucket", by chopping off the seconds, chopping off the minutes, chopping off the time, etc.
Basically, we're taking a timestamp (e.g. '2012-07-25 23:15:30') and assigning it to
minute '2012-07-25 23:15'
hour '2012-07-25 23'
day '2012-07-25'
A timestamp of '2012-07-25 23:25:00' would get assigned to
minute '2012-07-25 23:25'
hour '2012-07-25 23'
day '2012-07-25'
Then we go through and count the number of distinct buckets we assigned a timestamp to. If that's all the hits for this user in the month, the query would return a 2 for minutes, and a 1 for all other period counts.
For a user with a single hit within the month, all the counts for that user will be a 1.
For a user that has all their "hits" within exactly the same minute, the query will again return a 1 for all the counts.
(For a user with no "hits" within a month, no row will be returned. (You'd need to join another row source to get a list of users, if you wanted to return zero counts.)
For a user with a "hit" every second within a single day, this query will return counts like that shown for userid 2 in your example.
This result set gives you a kind of an indication of a user's activity for a month... how many "minute periods" within a month the user was active.
The largest value that could be returned for "days" would be the number of days in the month. The largest possible value to be returned for "hours" would be 24 times the number of days in the month times. The largest possible value returned for "minutes" would be 1440 times the number of days in the month.
But again, it's not entirely clear to me what result set you want to return. But this seems like a much more reasonable result set than the one from the previously "selected" answer.
SELECT userid, SUM(MINUTE(timestamp)) AS minutes, SUM(MINUTE(timestamp))/60 AS hours, SUM(MINUTE(timestamp))/(60*24) AS days, SUM(MINUTE(timestamp))/(60*24*7) AS weeks
FROM Table
GROUP BY userid
If neccesary, use ROUND(SUM(MINUTE(timestamp)), 0) if you want integer numbers.

Sum over Timerange overlapping to next day over several days

I am desperately trying to get sum of values from range of time over several days, the problem is the range overlaps a day e.g from 15:00 to 10:00 but unfortunately I cant come up with another solution than a loop over all days but there sure is a more elegant way to do this all in one query.
For a single day I have something like this
SELECT
(Date(`Date`)) AS `Date`, SUM(`Val`), `Ld_id`
FROM
(SELECT
`Date`, SUM(`Val`) AS `Val`, `Ld_id`
FROM
`tblVals`
INNER JOIN (SELECT
*
FROM
`tblDate`
WHERE
`Date` BETWEEN (SELECT CONCAT('2011-08-26 ', '14:31:00'))
AND (SELECT CONCAT('2011-08-27 ', '10:01:00'))
ORDER BY `Date` ASC) AS `A` ON `tblVals`.`date_id` = `A`.`date_id`
WHERE
`Ld_id` BETWEEN (SELECT
MIN(`Ld_id`)
FROM
`tblLr`
WHERE
`s_id` = '1') AND (SELECT
MAX(`Ld_id`)
FROM
`tblLr`
WHERE
`s_id` = '1')
GROUP BY ((60/30)*HOUR(`Date`)+FLOOR(MINUTE(`Date`)/30)),`Ld_id`
ORDER BY `Ld_id` ASC ,`Date` ASC) AS `A`
Group by `Ld_id`
Many thanks in advance for any hint`
If you want 15:00 to 14:59 the next day:
- subtract 15 hours from all timestamps
- group by the date part of the timestamp
If you want 15:00 to 10:00 the next day:
- subtract 15 hours from all timestamps
- discard all records where the timepart is now greater than 19:00
- group by the date part of the timestamp
In MySQL, I think it's something like this...
SELECT
DATE(DATE_SUB('Date', INTERVAL 15 HOUR)),
SUM(VAL)
FROM
tblVals
WHERE
TIME(DATE_SUB('Date', INTERVAL 15 HOUR)) < '19:00'
GROUP BY
DATE(DATE_SUB('Date', INTERVAL 15 HOUR))