How to get TIMEs of intervals between 2 given fields in MySQL - mysql

I have to following table, where start_time and end_time are TIME fields:
____________________________________
| id | day | start_time | end_time |
|____|_____|____________|____________|
| 1 | 1 | 8:00 | 12:00 |
I'd like to obtain the start time of every interval of 60 minutes between start_time and end_time, as:
_______
| start |
|_______|
| 8:00 |
| 9:00 |
| 10:00 |
| 11:00 |
Is that possible? Thanks

If you have a table that has integers, this is easier. If the field were datetime, you could do it like this (for up to 7 hours):
select date_add(t.start_time, interval n.n hour)
from t join
(select 0 as n 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
) n
on date_add(t.start_time, interval n.n hour) <= t.end_time
With time, you can do it by converting to seconds and doing the arithmetic there:
select sec_to_time((floor(time_to_sec(t.start_time - 1)/3600)+1) + n.n*3600)
from t join
(select 0 as n 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
) n
on (floor(time_to_sec(t.start_time - 1)/3600)+1) + n.n*3600 <= t.end_time

Related

List dates from specific mysql table and return 0 if day does not exists

I'm trying to generate a result from a query that list the last 7 days from today (2020/07/15) and the views matching a specific code.
If in that day the code has no views, I want the day to return 0.
Table Format
DAY | CODE | VIEWS
2020-07-10 | 123 | 5
2020-07-11 | 123 | 2
2020-07-12 | 123 | 3
2020-07-15 | 123 | 8
2020-07-15 | 124 | 2
2020-07-15 | 125 | 2
Expected result from code 123
DAY | VIEWS
2020-07-09 | 0
2020-07-10 | 5
2020-07-11 | 2
2020-07-12 | 3
2020-07-13 | 0
2020-07-14 | 0
2020-07-15 | 8
I already found a way to generate the calendar dates from here and adjust to my needs, but I don't know how to join the result with my table.
select * from
(select
adddate(NOW() - INTERVAL 7 DAY, t0) day
from
(select 1 t0
union select 1
union select 2
union select 3
union select 4
union select 5
union select 6
union select 7) t0) v
Any help would by apreceated.
One option uses a recursive query - available in MySQL 8.0:
with recursive cte as (
select current_date - interval 6 day dt
union all
select dt + interval 1 day from cte where dt < current_date
)
select c.dt, coalesce(sum(t.views), 0) views
from cte
left join mytable t on t.day = c.dt
group by c.dt
order by c.dt
You can also manually build a derived table, as you originaly intended to (this would work on all versions of MySQL):
select current_date - interval d.n day dt, coalesce(sum(t.views), 0) views
from (
select 0 n
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
) d
left join mytable t on t.day = current_date - interval d.n day
group by d.n
order by d.n desc

SQL Track Open Appointment

I am a beginner of SQL, I am working on a query that generates the Open Appointment Time Slot for each Provider (Employee). Can anyone help to with the following query? Thank you very much!
Schedule table sample Data:
ScheduleNum | ProvNum (Provider) | SchedDate | StartTime | EndTime
1 | 3 | 7/1/2017 | 8:00AM | 9:00 AM
2 | 3 | 7/1/2017 | 10:00AM | 11:00 AM
3 | 4 | 7/1/2017 | 8:00AM | 9:00 AM
4 | 4 | 7/1/2017 | 12:00PM | 1:00 PM
5 | 4 | 7/1/2017 | 3:00PM | 4:00 PM
Open Appointment Query Table Expected Result:
ProvNum (Provider) | SchedDate | avail_start_dt_time| avail_end_dt_time
3 | 7/1/2017 | 9:00 AM | 10:00 AM
4 | 7/1/2017 | 9:00 AM | 12:00 PM
4 | 7/1/2017 | 1:00 PM | 3:00 PM
Query that I am working on.
SELECT
avail.schedulenum
, avail.provnum
, avail.start_at as avail_start_dt_time
, avail.end_at as avail_end_dt_time
, avail.on_dt as just_date_no_time
, avail.starttime as prov_start_time
, avail.endtime as prov_end_time
FROM (
SELECT
dts.num, dts.on_dt, dts.start_at, dts.end_at, s.provnum, s.starttime, s.stoptime
FROM (
/* generates 1000, 10 minute "slots", starting at a variable date */
SELECT
schedulenum + 1 as num
, DATE_ADD(INTERVAL (slots.num * 10) MINUTE) start_at
, DATE_ADD(INTERVAL ((slots.num+ 1) * 10) MINUTE) end_at
FROM (
/* generates 1000 rows 0 to 999 */
SELECT hundreds.digit * 100 + tens.digit * 10 + ones.digit AS num
FROM (
SELECT 0 AS digit 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
) ones
CROSS JOIN (
SELECT 0 AS digit 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
) tens
CROSS JOIN (
SELECT 0 AS digit 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
) hundreds
) slots
) dts
INNER JOIN schedule s ON s.starttime < dts.end_at AND s.stoptime > dts.start_at
) avail
WHERE schedulenum IS NULL

MySQL: count unique days in period with overlaps and gaps (optimized)

How to calculate the number of unique days per month from a table with two date columns, in which the periods can have gaps and overlaps?
I rather not use a calendar table to get the unique days, because it generates a temporary table with thousands of records, and resources are limited.
Example table:
+---------+------------+------------+
| mygroup | alpha | omega |
+---------+------------+------------+
| 1 | 2017-02-04 | 2017-04-14 |
| 1 | 2017-03-25 | 2017-03-28 |
| 1 | 2017-01-23 | 2017-01-25 |
| 2 | 2017-02-05 | 2017-02-20 |
| 1 | 2017-04-28 | 2017-05-12 |
| etc. | etc. | etc. |
+---------+------------+------------+
Is it what you need?
select count(distinct selected_date),te.mygroup, MONTHNAME(selected_date)from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
cross join test te
where selected_date between te.alpha and te.omega
group by mygroup, MONTHNAME(selected_date)
Оutput for your example:
'17','1','April'
'25','1','February'
'3','1','January'
'31','1','March'
'12','1','May'
'16','2','February'
Count may be greater than number of the days in month because such overlap exists in few rows - it's not а mistake.
There is another way to do this roughly 10 times faster than a calendar table.
The biggest resource spoiler is the calendar table itself, it is used to filter unique days.
But instead of using a whole table record for that, it can be done using 31 bits in an UINT.
Recepe:
Create a calendar table with months only
Cut periods in months, and join them with the calendar table
Translate periods to bits of UINTs
OR the UINTs per month for uniqueness
Count their bits as unique days per month
Output:
+--------------+---------+---------+-------+
| Period | Group 1 | Group 2 | Total |
+--------------+---------+---------+-------+
| 2017 month 5 | 11 | 0 | 11 |
| 2017 month 4 | 15 | 0 | 15 |
| 2017 month 3 | 30 | 0 | 30 |
| 2017 month 2 | 24 | 15 | 39 |
| 2017 month 1 | 2 | 0 | 2 |
+--------------+---------+---------+-------+
MySQL query:
SELECT
`tabulate`.`period` AS `Period`,
SUM(IF(`tabulate`.`mygroup` = 1,
`tabulate`.`days`, 0)) AS `Group 1`,
SUM(IF(`tabulate`.`mygroup` = 2,
`tabulate`.`days`, 0)) AS `Group 2`,
SUM(`tabulate`.`days`) AS `Total`
FROM
( SELECT
`unique`.`period`,
BIT_COUNT(BIT_OR(CONV(CONCAT(
REPEAT("1", DAYOFMONTH(`unique`.`omega`) - DAYOFMONTH(`unique`.`alpha`)),
REPEAT("0", DAYOFMONTH(`unique`.`alpha`) - 1)
), 2, 10))) AS `days`,
`unique`.`mygroup`
FROM
( SELECT
DATE_FORMAT(`permonth`.`period_alpha`, "%Y month %c") AS `period`,
GREATEST(`permonth`.`period_alpha`, `permonth`.`example_alpha`) AS `alpha`,
LEAST(`permonth`.`period_omega`, `permonth`.`example_omega`) AS `omega`,
`permonth`.`mygroup`
FROM
( SELECT
`period`.`alpha` AS `period_alpha`,
DATE_SUB(`period`.`omega`, INTERVAL 1 DAY) AS `period_omega`,
`example`.`mygroup`,
IFNULL(`example`.`alpha`, `period`.`alpha`) AS `example_alpha`,
IFNULL(`example`.`omega`, CURDATE()) AS `example_omega`
FROM
( SELECT
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL `season`.`n` + (`month`.`n` << 2) MONTH
) AS `alpha`,
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL 1 + `season`.`n` + (`month`.`n` << 2) MONTH
) AS `omega`
FROM
( SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
) AS `month`
CROSS JOIN (SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
) AS `season`
) AS `period`
INNER JOIN
( SELECT 1 AS `mygroup`, "2017-02-04" AS `alpha`, "2017-04-14" AS `omega`
UNION ALL SELECT 1, "2017-03-25", "2017-03-28"
UNION ALL SELECT 1, "2017-01-23", "2017-01-25"
UNION ALL SELECT 2, "2017-02-05", "2017-02-20"
UNION ALL SELECT 1, "2017-04-28", "2017-05-12"
) AS `example` ON (
(`example`.`alpha` < `period`.`omega` OR `example`.`alpha` IS NULL)
AND IFNULL(`example`.`omega`, CURDATE()) >= `period`.`alpha`
)
) AS `permonth`
) AS `unique`
GROUP BY
`unique`.`period`,
`unique`.`mygroup`
) AS `tabulate`
GROUP BY `tabulate`.`period`
ORDER BY `tabulate`.`period` DESC

Find all Sundays in mySQL between January - March, 2016

What query to get all the Sundays date between January - March 2016?
so output just the shown date only.
| Sundays |
|- - - - - -|
| 2 |
| 9 |
| 16 |
| 23 |
| 30 |
- - - - - -
i find query like this select ('2013-04-15' - interval dayofweek('2013-08-15') - 1 day) - interval (weekofyear('2013-04-15') - 1) * 7 day as SUNDAY;
but just shown one sunday.
To get the Sunday below query will work for you
select DATE_ADD('2016-01-01', INTERVAL ROW DAY) as Date,
row+1 as DayOfMonth
from
(
SELECT #row := #row + 1 as row
FROM
(
select 0
union all
select 1
union all
select 3
union all
select 4
union all
select 5
union all
select 6
) t1,
(
select 0
union all
select 1
union all
select 3
union all
select 4
union all
select 5
union all
select 6
) t2,
(SELECT #row:=-1
) t3 limit 31
) b
where
DATE_ADD('2016-01-01', INTERVAL ROW DAY)
between '2016-01-01' and '2016-03-31'
and
DAYOFWEEK(DATE_ADD('2016-01-01', INTERVAL ROW DAY))=1;

SQL/Mysql query available dates and times in database

I need some help querying my calendar/dates table
Scenario: I have a "calendar" table with dates and times (see below), users will set their available dates, usually day by day with available time slots for each day. So my table looks like this:
+------+------------+---------------------+---------------------+
| ID | user_id | start_date | end_date |
+------+------------+---------------------+---------------------+
| 1 | 1 | 2016-09-01 08:00:00 | 2016-09-01 16:00:00 |
| 2 | 1 | 2016-09-03 00:00:00 | 2016-09-03 23:59:59 |
| 3 | 1 | 2016-09-04 00:00:00 | 2016-09-04 16:00:00 |
| 4 | 1 | 2016-09-05 08:00:00 | 2016-09-05 16:00:00 |
| 5 | 2 | 2016-09-05 08:00:00 | 2016-09-05 16:00:00 |
| 6 | 2 | 2016-09-07 08:00:00 | 2016-09-07 16:00:00 |
| 7 | 2 | 2016-09-08 08:00:00 | 2016-09-08 16:00:00 |
| 8 | 2 | 2016-09-08 18:00:00 | 2016-09-08 22:00:00 |
+------+------------+---------------------+---------------------+
We have 2 users here so I want the following:
If I search for start_date = 2016-09-05 08:00:00 and end_date = 2016-09-05 16:00:00 it should return user 1 and 2. Since both of them has an entry with these dates. Same goes as well if start_date = 2016-09-05 09:00:00 and end_date = 2016-09-05 15:00:00, this should as well return both users since the time im searching for is between the time slots as shown in the example.
Second scenario is a little bit more tricky, If user search for start_date = 2016-09-03 08:00:00 and end_date = 2016-09-04 16:00:00 i want the query to check the following:
see if the user is available each day at these times.
so in this case, is the user available on 2016-09-03 between 08:00:00 and 16:00:00 and as well on 2016-09-04 between 08:00:00 and 16:00:00.
In the example over this should return user 1.
Im open for suggestion on re-designed my schema if needed.
Hope some can help me with this.
DEMO include some aditional code comment, and show how the query evolve . Also I add another row for user_id = 2 to show how only match one of the two days in the range.
SELECT U.`user_id`
FROM (
select a.selectDate,
CONCAT(a.selectDate, ' ', time(#s_date)) as start_time,
CONCAT(a.selectDate, ' ', time(#s_date)) as end_time
from (
select '1900-01-01' + INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) + (10000 * e.a)) DAY as selectDate
from (select 0 as a 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) as a
cross join (select 0 as a 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) as b
cross join (select 0 as a 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) as c
cross join (select 0 as a 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) as d
cross join (select 0 as a 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) as e
) a
CROSS JOIN (SELECT #s_date := '2016-09-03 08:00:00', #e_date := '2016-09-04 16:00:00') par
-- CROSS JOIN (SELECT #s_date := '2016-09-05 08:00:00', #e_date := '2016-09-05 16:00:00') par
-- CROSS JOIN (SELECT #s_date := '2016-09-05 09:00:00', #e_date := '2016-09-05 15:00:00') par
WHERE selectDate BETWEEN date(#s_date)
AND date(#e_date)
) D
CROSS JOIN (SELECT DISTINCT `user_id` FROM Table1) U
LEFT JOIN Table1 T
ON U.`user_id` = T.`user_id`
AND D.start_time <= T.`end_date`
AND D.end_time >= T.`start_date`
GROUP BY U.`user_id`
HAVING COUNT(U.`user_id`) = COUNT(T.`user_id`);
OUTPUT
Step 1: create a list of dates, in this case 273 years
Step 2: select all dates between the range define in the parameters, also include the time window to each date.
Step 3: join all together to see what dates have user in that time window
Step 4: select only user with a time window for all dates