MySQL: a timespan available within consecutive times - mysql

I have a table with consecutive times and dates for an agenda.
ID date begin_time end_time
1 05-02-15 19:00:00 19:05:00
2 05-02-15 19:05:00 19:10:00
3 05-02-15 19:10:00 19:15:00
4 05-02-15 19:15:00 19:20:00
5 05-02-15 19:20:00 19:25:00
6 05-02-15 19:25:00 19:30:00
7 05-02-15 19:30:00 19:35:00
8 05-02-15 19:35:00 19:40:00
9 06-02-15 19:00:00 19:05:00
10 06-02-15 19:05:00 19:10:00
11 06-02-15 19:10:00 19:15:00
12
13
14 06-02-15 19:25:00 19:30:00
15 06-02-15 19:30:00 19:35:00
16 06-02-15 19:35:00 19:40:00
http://sqlfiddle.com/#!2/54d9f6
As you can see on 05-02-15 the time from 19:00 until 19:40 is consecutive
As you can see on 06-02-15 the time from 19:00 until 19:15 is consecutive
As you can see on 06-02-15 the time from 19:25 until 19:40 is consecutive
The begin time and end time always have a 5 minute difference.
I want to have all the dates, that have a consecutive timespan of x minutes. So when x = 30, the result is:
05-02-15
when x = 10, the result is:
05-02-15
06-02-15
Idea for an approach
Maybe the first step is to get all the consecutive parts, secondly count the number of records in a part (when x = 30, we need at least 30 min. / 5 min. = 6).

This query check if you have #x/5 free slots in next #x minutes. And if so, than them cover whole #x minutes interval, means them are consecutive.
set #x=15;
select distinct t1.date
from
`agenda_specialists` as t1 join
`agenda_specialists` as t2 on
t2.date=t1.date and
t2.begin>=t1.begin and
t2.begin<addtime(t1.begin,sec_to_time(#x*60))
group by t1.id
having count(*)=#x/5
http://sqlfiddle.com/#!2/54d9f6/50

User variables are very useful in mysql
SELECT date,MIN(CASE WHEN BEGIN!='00:00:00' THEN BEGIN END) m,
MAX(CASE WHEN END!='00:00:00' THEN END END) mm
FROM
( SELECT BEGIN,END,date, CASE
WHEN
END = BEGIN +INTERVAL 5 MINUTE THEN #n ELSE #n:=#n+1 END AS g
FROM agenda_specialists,
(SELECT #n:=0) x
ORDER BY id) s
GROUP BY date, g
HAVING ((TIME_TO_SEC(mm) - TIME_TO_SEC(m))/60)>=40
Esentially you want to check if the begin is equal with the end+5 minutes if not you increment a variable you can GROUP BY,it creates the intervals besides the different date.The rest is easy.I had to change the collations,for some reason it gave me illegal mix of collation.Just play with the number at the end.
FIDDLE
Edit:
Run
SHOW VARIABLES LIKE '%char%';
You probably have character_set_server='latin1'
Go into your config file,my.cnf and add or uncomment these lines:
character-set-server = utf8
collation-server = utf8_general_ci
Tested on my machine 5.5

Related

Fetch data between morning and evening

im trying to fetch the data between two times, i.e., morning (7:30AM to 7:30PM) evening (7:30PM to 7:30AM).
required only hour based output.{ > 7:30 < 19:30 as morning and > 19:30 < 7:30 as night)
Please suggest the query
Tablename: data
----------------
created_date services_name id
28-08-2022 18:54 KANE 1
28-08-2022 19:00 BAPLO 2
28-08-2022 23:22 BAPLO 3
28-08-2022 23:40 VLOTLS 4
29-08-2022 00:02 DELLP 5
29-08-2022 00:42 SECON 6
29-08-2022 02:00 BAPLO 7
29-08-2022 03:00 PRODC 8
29-08-2022 05:14 DELLP 9
29-08-2022 05:30 SECON 10
29-08-2022 05:42 SECON 11
im using below command to fetch output
SELECT
CONCAT( HOUR(created_date), ' to ', CONCAT( HOUR(created_date), ':59:59' ) ) as time_frame,
COUNT(*)
FROM
data
GROUP BY
DATE(created_date),
HOUR(created_date)
ORDER BY
DATE(created_date),
HOUR(created_date)
Out as below
0 to 0:59:59 2
2 to 2:59:59 1
3 to 3:59:59 1
5 to 5:59:59 3
18 to 18:59:59 1
19 to 19:59:59 1
23 to 23:59:59 2
But i required as
morning ( between 07:30 to 19:30) count is 2
evening ( between 19:30 to 07:30) count is 9
You can perform a UNION to get two separate counts, one for morning times and one evening times. Within the WHERE clause, you can convert created_date to a time format using the TIME() data type and then plug in your morning and evening times within the BETWEEN condition.
Note that you have to use the NOT operator to negate the BETWEEN operator for the evening count because BETWEEN works within a range (small to large 0..1, not large to small 1..0) so the NOT operator will essentially get everything else outside of the morning BETWEEN range.
SELECT 'Morning' as time_frame, COUNT(*) AS `count` FROM temp
WHERE TIME(created_date)
BETWEEN '07:30:00' AND '19:30:00'
UNION
SELECT 'Evening' as time_frame, COUNT(*) AS `count` FROM temp
WHERE TIME(created_date)
NOT BETWEEN '07:30:00' AND '19:30:00'
Output:
time_frame
count
Morning
2
Evening
9
See Fiddle.

Calculate total scheduled against total actual in two separate tables

I have two tables in my schema. The first contains a list of recurring appointments - default_appointments. The second table is actual_appointments - these can be generated from the defaults or individually created so not linked to any default entry.
Example:
default_appointments
id
day_of_week
user_id
appointment_start_time
appointment_end_time
1
1
1
10:00:00
16:00:00
2
4
1
11:30:00
17:30:00
3
6
5
09:00:00
17:00:00
actual_appointments
id
default_appointment_id
user_id
appointment_start
appointment_end
1
1
1
2021-09-13 10:00:00
2021-09-13 16:00:00
2
NULL
1
2021-09-13 11:30:00
2021-09-13 13:30:00
3
6
5
2021-09-18 09:00:00
2021-09-18 17:00:00
I'm looking to calculate the total minutes that were scheduled in against the total that were actually created/generated. So ultimately I'd end up with a query result with this data:
user_id
appointment_date
total_planned_minutes
total_actual_minutes
1
2021-09-13
360
480
1
2021-09-16
360
0
5
2021-09-18
480
480
What would be the best approach here? Hopefully the above makes sense.
Edit
OK so the default_appointments table contains all appointments that are "standard" and are automatically generated. These are what appointments "should" happen every week. So e.g. ID 1, this appointment should occur between 10am and 4pm every Monday. ID 2 should occur between 11:30am an 5:30pm every Thursday.
The actual_appointments table contains a list of all of the appointments which did actually occur. Basically what happens is a default_appointment will automatically generate itself an instance in the actual_appointments table when initially set up. The corresponding default_appointment_id indicates that it links to a default and has not been changed - therefore the times on both will remain the same. The user is free to change these appointments that have been generated by a default, resulting in setting the default_appointment_id to NULL * - or -* can add new appointments unrelated to a default.
So, if on a Monday (day_of_week = 1) I should normally have a default appointment at 10am - 4pm, the total minutes I should have planned based on the defaults are 360 minutes, regardless of what's in the actual_appointments table, I should be planned for those 360 minutes every Monday without fail. If in the system I say - well actually, I didn't have an appointment from 10am - 4pm and instead change it to 10am - 2pm, actual_appointments table will then contain the actual time for the day, and the actual minutes appointed would be 240 minutes.
What I need is to group each of these by the date and user to understand how much time the user had planned for appointments in the default_appointments table vs how much they actually appointed.
Adjusted based on new detail in the question.
Note: I used day_of_week values compatible with default MySQL behavior, where Monday = 2.
The first CTE term (args) provides the search parameters, start date and number of days. The second CTE term (drange) calculates the dates in the range to allow generation of the scheduled appointments within that range.
allrows combines the scheduled and actual appointments via UNION to prepare for aggregation. There are other ways to set this up.
Finally, we aggregate the results per user_id and date.
The test case:
Working Test Case (Updated)
WITH RECURSIVE args (startdate, days) AS (
SELECT DATE('2021-09-13'), 7
)
, drange (adate, days) AS (
SELECT startdate, days-1 FROM args UNION ALL
SELECT adate + INTERVAL '1' DAY, days-1 FROM drange WHERE days > 0
)
, allrows AS (
SELECT da.user_id
, dr.adate
, ROUND(TIME_TO_SEC(TIMEDIFF(da.appointment_end_time, da.appointment_start_time))/60, 0) AS planned
, 0 AS actual
FROM drange AS dr
JOIN default_appointments AS da
ON da.day_of_week = dayofweek(adate)
UNION
SELECT user_id
, DATE(appointment_start) AS xdate
, 0 AS planned
, TIMESTAMPDIFF(MINUTE, appointment_start, appointment_end)
FROM drange AS dr
JOIN actual_appointments aa
ON DATE(appointment_start) = dr.adate
)
SELECT user_id, adate
, SUM(planned) AS planned
, SUM(actual) AS actual
FROM allrows
GROUP BY adate, user_id
;
Result:
+---------+------------+---------+--------+
| user_id | adate | planned | actual |
+---------+------------+---------+--------+
| 1 | 2021-09-13 | 360 | 480 |
| 1 | 2021-09-16 | 360 | 0 |
| 5 | 2021-09-18 | 480 | 480 |
+---------+------------+---------+--------+

MySql - count records since last time it was X o'clock

I need to count records created since it was last 9:00am.
so at 8:59am I need to count all records since yesterday at 9:00am and at 9:01am I will need to count since today at 9:00am,
data sample
ID | created
----------------------------------
1 | 2018-11-13 17:00
2 | 2018-11-13 09:00
3 | 2018-11-13 08:01
4 | 2018-11-12 13:00
5 | 2018-11-11 17:31
running the query at 13-11-2018 8:59am should return 2 (rows 3,4)
running the query at 13-11-2018 9:01am should return 1 (rows 2)
the query I'm looking for should be something like:
SELECT count(id) FROM myTable WHERE created > "TIME_SINCE_9AM()"
any help?
I figured out a solution, I hoped for a prettier query but it works...
I'm using IF statement to determine if right now is before 9am or after and running count on records accordingly, (the H var is for testing purposes, if you put 18 in there it works since last 18:00)
SET #H = "9";
SELECT
IF (TIMEDIFF(NOW(), SUBDATE(CURDATE(),INTERVAL (-#H) HOUR))<0,
SUM(CASE WHEN created>SUBDATE(CURDATE(),INTERVAL (-#H+24) HOUR) THEN 1 ELSE 0 END),
SUM(CASE WHEN created>SUBDATE(CURDATE(),INTERVAL (-#H) HOUR) THEN 1 ELSE 0 END)
) counter
FROM mytable

Does any way to get the last inserted values in each days

id date calls
5 2015-02-17 01:06:01 1
6 2015-02-17 11:07:01 2
7 2015-02-17 23:06:01 3
8 2015-02-18 03:07:01 1
9 2015-02-18 09:06:01 2
10 2015-02-18 17:07:01 3
11 2015-02-18 22:06:01 4
12 2015-02-19 01:07:01 1
13 2015-02-19 08:06:01 2
14 2015-02-19 18:07:01 3
15 2015-02-19 23:06:01 4
my table structure is like this and I need to calculate the sum of call in each days. In this table, you can see that, the last call in feb 17 was at 23:06:01 and call count was 3. In feb 18 was at 22:06:01 and call count was 4. Can I get the sum of all this last call counts of each day.
You can use a subquery to determine which rows to sum (the ones matching the last call for each date, using MySQL it would be:
select sum(calls) sum_last_calls
from your_table
where `date` in (
select max(date) max_date
from your_table
group by date(`date`)
)
This query will return 11 as the sum (from 3+4+4).
The date() function used in the subquery is specific to your database and might need to be changed according to your specific database syntax - the point is that it should return the date without time (it could be date::date (Postgresql) or cast(date as date) (MSSQL and others)).
Sample SQL Fiddle for MySQL and Postgresql
Postgresql version:
select sum(calls) as calls
from (
select max(calls) as calls
from t
where date::date between '2015-02-17' and '2015-02-19'
group by date::date
) s

SQL Server : using elements in a case statement that might be NULL

Okay, so I have a question about using columns in a case statement that might be null.
Again I have two tables the first looks like this UserActivity:
userID Action Time
1 25 12:00
1 10 12:01
1 12 12:35
1 6 13:54
2 10 6:47
2 42 6:48
3 8 11:54
etc.
But the second now looks like this UserSchedule:
userID startTime1 stopTime1 startTime2 stoptime2 startTime3 stopTime3
1 07:00 09:00 11:00 12:00 14:30 16:30
2 11:00 12:30 14:00 15:30
3 14:00 15:00
etc.
So I need to be able to evaluate when in relation to all of those start and stop times an Action took place. Some users will have up to 5 start/stop times in a day. Some will only have one.
This code was given to me to resolve an earlier issue by #Adam Wengler, and it works great if there is only one start and stop time. I'm unsure how to include the logic to test against each of the 5 potential start/stop columns and ignore them if they are empty.
UPDATE ua
SET ua.TimeStatusId = CASE
WHEN ua.Time >= us.Schedule_Start
AND ua.Time <= us.Schedule_stop THEN 1
WHEN ua.Time >= DATEADD(HOUR, -1, us.Schedule_Start)
AND ua.Time <= us.ScheduleStart THEN 0
WHEN ua.Time >= us.Schedule_Stop
AND ua.Time =< DATEADD(HOUR, 1, us.Schedule_Stop)
THEN 2
ELSE 3
END
FROM dbo.UserActivity AS ua
INNER JOIN dbo.userSchedule AS us ON ua.UserId = us.UserId
You can use the IsNull() function to resolve your issue