Mysql: get hour between selected time - mysql

I have a table like this:
id | date | time from | time to
1 | 2015-09-19 | 20:00 | 04:00
2 | 2015-09-19 | 10:00 | 23:00
3 | 2015-09-19 | 22:00 | 10:00
4 | 2015-09-20 | 10:00 | 16:00
For each row, I need hour between 22:00 and 06:00
Then first row has 6, second row has 1, third row has 8, fourth row has nothing.

Yes, it is possible. Just use some logic:
If time to is lower than time from then just use the amount of hours between midnight and time from but maximum 2 hours, and add the minimum between 6 hours (the max limit) and time to.
In time from is lower than time to then
-- if time from is greater than 6 add 0, else add time from
-- if time to is lower than 22 add 0, else add difference between 24 and time to.
SELECT
*,
CASE
WHEN time_to < time_from
THEN
TIME_TO_SEC (
LEAST(TIMEDIFF(TIME('24:00'), time_from), TIME('02:00')) +
LEAST(time_to, TIME('06:00'))
) / 60 / 60
ELSE
TIME_TO_SEC (
CASE WHEN time_from > TIME('06:00') THEN TIME('00:00') ELSE time_from END +
CASE WHEN time_to < TIME('22:00') THEN TIME('00:00') ELSE TIMEDIFF(TIME('24:00'), time_to) END
) / 60 / 60
END new_time_to
FROM demo;
Here is the example: http://sqlfiddle.com/#!9/78571/36/0

Related

How to get the data only on working hours

Is there a way to select or query the data only on working hours?
id
description
datetime
1
Alarm Activated
2022-01-02 14:00:00
2
Alarm Deactivated
2022-01-02 15:00:00
3
Alarm Activated
2022-01-03 18:00:00
..
Alarm Activated
2022-01-31 11:00:00
I'd like to get the number of the alarm activated on or during working hours from mon-fri 8am to 5pm.
I tried to use the between date but no luck.
SELECT * -- if you need to count them only - use SELECT COUNT(*)
FROM datatable
WHERE WEEKDAY(`datetime`) < 5 -- test weekday
AND TIME(`datetime`) BETWEEN '08:00:00' AND '17:00:00'; -- test hours
Here we use the following tests:
weekday < 6 (Saturday) = Monday to Friday
hour more than 7 (from 08:00:00)
hour less than 17 (to 16:59:59)
create table alarms(
id int,
description varchar(100),
date_time datetime);
insert into alarms values
(1,'Alarm Activated',
'2022-01-02 14:00:00'),
(2,'Alarm Deactivated',
'2022-01-02 15:00:00'),
(3,'Alarm Activated',
'2022-01-03 18:00:00'),
(4,'Alarm Activated',
'2022-01-31 11:00:00');
select *
from alarms
where weekday(date_time) < 6
and 7 < hour(date_time) < 17;
id | description | date_time
-: | :-------------- | :------------------
3 | Alarm Activated | 2022-01-03 18:00:00
4 | Alarm Activated | 2022-01-31 11:00:00
db<>fiddle here

Get End Time by Adding Number of Minutes in Start Time Keeping in mind the working hours

I have database table in which I have daily working hours of office.
What I need is to get End Time by Adding Number of minutes in Start Time keeping in mind the office hours. The working hours are from 09:00 to 17:30
I will be thankful if some one could help me to write query in mysql so that I could calculate end time.
my Sample Table is
+------------------+------------------+
| starttime | endtime |
+------------------+------------------+
| 2017-01-01 00:00 | 2017-01-01 00:00 |
| 2017-01-02 09:00 | 2017-01-02 17:30 |
| 2017-01-03 09:00 | 2017-01-03 17:30 |
| 2017-01-04 09:00 | 2017-01-04 17:30 |
| 2017-01-05 09:00 | 2017-01-05 17:30 |
| 2017-01-06 09:00 | 2017-01-06 17:30 |
| 2017-01-07 09:00 | 2017-01-07 14:30 |
| 2017-01-08 00:00 | 2017-01-08 00:00 |
| 2017-01-09 09:00 | 2017-01-09 17:30 |
+------------------+------------------+
Input time : 2017-01-02 16:52
adding minutes: 300
required time : 2017-01-03 12:22
If I understand clearly, you want:
SELECT CONCAT(FLOOR((540 + t.time)/60),'h ', MOD(540 + t.time, 60),'m') as HOURS
FROM table t;
Using DATE_ADD is what you want
SELECT DATE_ADD(starttime, INTERVAL 300 MINUTE) as endtime;
EDIT:
Okay, I think i understand you know. The code below could probably be condensed, however, I left it verbose for readability.
SELECT CASE WHEN
DATE_ADD(TIMESTAMP(starttime), INTERVAL 300 MINUTE)
BETWEEN TIMESTAMP(DATE(starttime),'09:00')
AND TIMESTAMP(DATE(starttime),'17:30')
THEN
## starttime plus 5 hours, is still in range of current business window
DATE_ADD(starttime, INTERVAL 300 MINUTE)
WHEN TIMESTAMP(starttime) > TIMESTAMP(DATE(starttime),'17:30') THEN
## startime falls after business hours, add it to the following day at 09:00
DATE_ADD(TIMESTAMP(DATE(starttime),'09:00'),
INTERVAL (1440 /*24 hours (i.e. next day)*/ + 300 ) MINUTE
)
ELSE
## if the starttime falls within business hours, but extends into the next business day,
## calculate the difference up to 17:30, add to the following day, after 09:00
DATE_ADD(TIMESTAMP(DATE(starttime),'09:00'),
INTERVAL (
1440 + /*24 hours (i.e. next day)*/
300 - /* standard working day */
ABS(
TIMESTAMPDIFF(MINUTE, TIMESTAMP(DATE(starttime),'17:30'), starttime)
) /* less yesterday's minutes */
) MINUTE
)
END AS endtime
;

Calculating "TotalshiftTiming - (Break01 + Lunch +Break02)

I have a table that displays shift start time, break 01 start time, break 01 end time, lunch start time, lunch end time, break 02 start time, break 02 end time,
shift end time.
Here I wanted to calculate everything in minutes.
Time = TotalShiftTime - ((break01Start - break01endtime) + (lunchstart - lunchend) + (break02start - break02end))
I want to calculate this in minutes on daily basis. I want to calculate this with respect to start date and end date which i wanted to give as input.
It has to be like
[(endDate - startDate) * Time]
If my endDate is 07/03/2018 and startDate is 04/03/2018 I want it to be [3 * Time].
PS: I don't have a column for startDate and endDate. I want to get cumulative minutes when I select system date.
How can I achieve this?
Shiftstart | Brk01start | Brk01end | LunchStart | LunchEnd | Brk02Start | Brk02end | Shiftend
8:00:00 | 11:15:00 | 11:45:00 | 13:00:00 | 13:30:00 | 15:15:00 | 15:30:00 | 17:00:00

date_add and repeat forever

I got an alert table for users, in which we have to send alerts to users in user defined intervals like 0 ( only once), 3 months, 6 months, 1 year
So I designed a table like this
id | user_id | alert_date | repeat_int
-----+--------------+-------------------------+-------------
12 | 747 | 2013-04-19 00:00:00 | 0
13 | 746 | 2013-03-19 00:00:00 | 1
14 | 745 | 2012-04-19 00:00:00 | 0
15 | 744 | 2013-04-19 00:00:00 | 0
16 | 743 | 2013-05-19 00:00:00 | 0
We are sending alert just a day before "alert_date"
With the following query I can fetch the data
SELECT al.id,
al.user_id,
al.alert_date,
al.repeat_int AS repunit
FROM alerts AS al
WHERE DATE_ADD(alert_date,INTERVAL repeat_int MONTH)=date_add(CURRENT_DATE,INTERVAL 1 DAY)
OR date(al.alert_date)=date_add(CURRENT_DATE,INTERVAL 1 DAY)
Its working file but my real problem is
The repeat will only works once, we need it repeat every interval
ie. If alert date is 2012-03-14 and repeat_int is 0 - Need to work only once
but if alert date is 2012-03-14 and repeat_int is 1 - Need to work in every 14th from 2012-03-14
and if the alert date is 2012-03-14 and repeat_int is 3 - Need to work in every three month's 14. ie alert on 2012-03-14, 2012-06-14, 2012-09-14 etc...
Is there any way to do that?
Update
The OP has changed his schema in response to comments, so the query is essentially:
SELECT *
FROM alerts
WHERE CURRENT_DATE + INTERVAL 1 DAY = COALESCE(next_alert_date, alert_date);
This handles "next_alert_date" being NULL on the very first run.
Original answer
For the original schema:
SELECT *
FROM alerts
JOIN (SELECT CURRENT_DATE + INTERVAL 1 DAY AS tomorrow) d
WHERE -- We want to alert if
-- 1. Tomorrow is the alert_date
tomorrow = alert_date
OR
--
-- 2. Tomorrow is "repeat_int" months removed from alert_date, falling on
-- the same day of the month or on the end of the month if the original
-- alert_date day of month is later in the month than is possible for us
-- now. E.g., 2013-01-31 repeated monthly is adjusted to 2013-02-28.
(
PERIOD_DIFF(DATE_FORMAT(tomorrow, '%Y%m'), DATE_FORMAT(alert_date, '%Y%m'))
MOD repeat_int = 0
AND
-- Make sure we are at the same day of the month
( (DAYOFMONTH(tomorrow) = DAYOFMONTH(alert_date)
OR
-- Or, if the day of the alert is beyond the last day of our month,
-- that we are at the end of our month.
(LAST_DAY(alert_date) > LAST_DAY(tomorrow)
AND
DAYOFMONTH(tomorrow) = LAST_DAY(tomorrow)) )
);

Grouping MySQL datetime into intervals irrespective of timezone

This question has been asked before but I am facing a slightly different problem.
I have a table which logs events and stores their timestamps (as datetime). I need to be able to break up time into chunks and get number of events that occurred in that interval. The interval can be custom (Say from 5 minutes to 1 hour and even beyond).
The obvious solution is to convert the datetime to unix_timestamp divide it by number of seconds in the interval, take its floor function and multiply it back by the number of seconds. Finally convert the unix_timestamp back to the datetime format.
This works fine for small intervals.
select
from_unixtime(floor(unix_timestamp(event.timestamp)/300)*300) as start_time,
count(*) as total
from event
where timestamp>='2012-08-03 00:00:00'
group by start_time;
This gives the correct output
+---------------------+-------+
| start_time | total |
+---------------------+-------+
| 2012-08-03 00:00:00 | 11 |
| 2012-08-03 00:05:00 | 4 |
| 2012-08-03 00:10:00 | 4 |
| 2012-08-03 00:15:00 | 7 |
| 2012-08-03 00:20:00 | 8 |
| 2012-08-03 00:25:00 | 1 |
| 2012-08-03 00:30:00 | 1 |
| 2012-08-03 00:35:00 | 3 |
| 2012-08-03 00:40:00 | 3 |
| 2012-08-03 00:45:00 | 5 |
~~~~~OUTPUT SNIPPED~~~~~~~~~~~~
But if I increase the interval to say 1 hour (3600 sec)
mysql> select from_unixtime(floor(unix_timestamp(event.timestamp)/3600)*3600) as start_time, count(*) as total from event where timestamp>='2012-08-03 00:00:00' group by start_time;
+---------------------+-------+
| start_time | total |
+---------------------+-------+
| 2012-08-02 23:30:00 | 35 |
| 2012-08-03 00:30:00 | 30 |
| 2012-08-03 01:30:00 | 12 |
| 2012-08-03 02:30:00 | 18 |
| 2012-08-03 03:30:00 | 12 |
| 2012-08-03 04:30:00 | 4 |
| 2012-08-03 05:30:00 | 3 |
| 2012-08-03 06:30:00 | 13 |
| 2012-08-03 07:30:00 | 269 |
| 2012-08-03 08:30:00 | 681 |
| 2012-08-03 09:30:00 | 1523 |
| 2012-08-03 10:30:00 | 911 |
+---------------------+-------+
The reason, as far as I could gauge, for the boundaries not being set properly is that unix_timestamp will convert time from my local timezone (GMT + 0530) to UTC and then output the numerical value.
So a value like 2012-08-03 00:00:00 will actually be 2012-08-02 18:30:00. Dividing and using floor will set the minutes part to 00. But when I use from_unixtime, it will convert it back to GMT + 0530 and hence give me intervals that begin at 30 mins.
How do I ensure the query works correctly irrespective of the timezone? I use MySQL 5.1.52 so to_seconds() is not available
EDIT:
The query should also fire correctly irrespective of the interval (can be hours, minutes, days). A generic solution would be appreciated
You can use TIMESTAMPDIFF to group by intervals of time:
For a specified interval of hours, you can use:
SELECT '2012-08-03 00:00:00' +
INTERVAL FLOOR(TIMESTAMPDIFF(HOUR, '2012-08-03 00:00:00', timestamp) / <n>) * <n> HOUR AS start_time,
COUNT(*) AS total
FROM event
WHERE timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time
Replace the occurances of 2012-08-03 00:00:00 with your minimum input date.
<n> is your specified interval in hours (every 2 hours, 3 hours, etc.), and you can do the same for minutes:
SELECT '2012-08-03 00:00:00' +
INTERVAL FLOOR(TIMESTAMPDIFF(MINUTE, '2012-08-03 00:00:00', timestamp) / <n>) * <n> MINUTE AS start_time,
COUNT(*) AS total
FROM event
WHERE timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time
Where <n> is your specified interval in minutes (every 45 minutes, 90 minutes, etc).
Be sure you're passing in your minimum input date (in this example 2012-08-03 00:00:00) as the second parameter to TIMESTAMPDIFF.
EDIT: If you don't want to worry about which interval unit to pick in the TIMESTAMPDIFF function, then of course just do the interval by seconds (300 = 5 minutes, 3600 = 1 hour, 7200 = 2 hours, etc.)
SELECT '2012-08-03 00:00:00' +
INTERVAL FLOOR(TIMESTAMPDIFF(SECOND, '2012-08-03 00:00:00', timestamp) / <n>) * <n> SECOND AS start_time,
COUNT(*) AS total
FROM event
WHERE timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time
EDIT2: To address your comment pertaining to reducing the number of areas in the statement where you have to pass in your minimum parameter date, you can use:
SELECT b.mindate +
INTERVAL FLOOR(TIMESTAMPDIFF(SECOND, b.mindate, timestamp) / <n>) * <n> SECOND AS start_time,
COUNT(*) AS total
FROM event
JOIN (SELECT '2012-08-03 00:00:00' AS mindate) b ON timestamp >= b.mindate
GROUP BY start_time
And simply pass in your minimum datetime parameter once into the join subselect.
You can even make a second column in the join subselect for your seconds interval (e.g. 3600) and name the column something like secinterval... then change the <n>'s to b.secinterval, so you only have to pass in your minimum date parameter AND interval one time each.
SQLFiddle Demo
the easier method would be:
Method1
select date(timestamp) as date_timestamp, hour(timestamp) as hour_timestamp, count(*) as total
from event
where timestamp>='2012-08-03 00:00:00'
group by date_timestamp, hour_timestamp
if you would like to use your original approach.
Method2
select from_unixtime(floor(unix_timestamp(event.timestamp-1800)/3600)*3600+1800) as start_time,
count(*) as total
from event
where timestamp>='2012-08-03 00:00:00'
group by start_time;
EDIT1
for the first method, it also allows user to set different interval.
For example, if user wants the log to group by 15 minutes,
select date(time) as date_timestamp,
hour(time) as hour_timestamp,
floor(minute(time) as minute_timestamp / 15) * 15 as minute_timestamp
count(*) as total
from event
group by date_timestamp, hour_timestamp, minute_timestamp