Table "Working periods":
id
started_at
ended_at
1
2022-06-09 08:00:00
2022-06-09 12:00:00
2
2022-06-09 13:00:00
2022-06-09 16:00:00
Table "Appointments":
id
started_at
ended_at
1
2022-06-09 08:00:00
2022-06-09 09:00:00
2
2022-06-09 09:00:00
2022-06-09 10:00:00
3
2022-06-09 15:00:00
2022-06-09 16:00:00
I need to find free periods of time within "Working periods" and excluding "Appointments" using only one MySQL query (statement). So, the query should result in the following table:
id
started_at
ended_at
1
2022-06-09 10:00:00
2022-06-09 12:00:00
2
2022-06-09 13:00:00
2022-06-09 15:00:00
I have tried the following query, which does almost what I need, but is tied to only one "Working period" defined with 2 hardcoded variables:
set #timeSlider='2022-06-09 08:00:00';
set #timeMaximum='2022-06-09 15:00:00';
select if (d.name = 'free', #timeSlider, a.started_at) as started_at,
if (d.name = 'free', a.started_at, #timeSlider := a.ended_at) as ended_at,
d.name as `status`
from (select 1 as place, 'free' as name union select 2 as place, 'busy' as name) as d
inner join appointments a
having started_at < ended_at AND `status` = 'free'
union select #timeSlider as started_at, #timeMaximum as ended_at, 'free' as `status`
from (select 1) as d
where #timeSlider < #timeMaximum
order by started_at, ended_at;
Is there a way to set the variables for each "Working period" separately and fit the entire query in 1 SQL statement? Or maybe there is another way to solve the problem?
Related
EDIT: I have added the primary key, following the comment by #Strawberry
The aim is to return the number of current members, and also the number of past memberships, on any particular date/time.
For example, suppose we have
msid id start cancelled
1 1 2020-01-01 09:00:00 null
2 2 2020-01-01 09:00:00 2020-12-31 09:00:00
3 2 2021-01-01 09:00:00 null
4 3 2020-01-01 09:00:00 2020-06-30 09:00:00
5 3 2020-02-01 09:00:00 2020-06-30 09:00:00
6 3 2020-07-01 09:00:00 null
and we want to calculate the number of members at various times, which should return as follows
Datetime Current Past <Notes - not to be returned by the query>
2020-01-01 12:00:00 3 0 -- all 3 IDs have joined earlier on this date
2020-02-01 12:00:00 3 0 -- new membership for existing member (ID 3) is not counted
2020-06-30 12:00:00 2 1 -- ID 3 has cancelled earlier on this day
2020-07-01 12:00:00 3 0 -- ID 3 has re-joined earlier on this day
2020-12-31 12:00:00 2 1 -- ID 2 has cancelled earlier on this day
2021-01-01 12:00:00 3 0 -- ID 2 has re-joined earlier on this day
An ID may either be current or past, but never both. That is, if a past member re-joins, as in the case of ID 2 and 3 above, they become current members, and are no longer past members.
Also, a member may have multiple current memberships, but they can only be counted as a current member once, as in the case of ID 3 above.
How can this be achieved in MySQL ?
Here is a db<>fiddle with the above data
Test this:
WITH
cte1 AS ( SELECT start `timestamp` FROM dt
UNION
SELECT cancelled FROM dt WHERE cancelled IS NOT NULL ),
cte2 AS ( SELECT DISTINCT id
FROM dt )
SELECT cte1.`timestamp`, COUNT(DISTINCT dt.id) current, SUM(dt.id IS NULL) past
FROM cte1
CROSS JOIN cte2
LEFT JOIN dt ON cte1.`timestamp` >= dt.start
AND (cte1.`timestamp` < dt.cancelled OR dt.cancelled IS NULL)
AND cte2.id = dt.id
GROUP BY cte1.`timestamp`
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=942e4c97951ed0929e178134ef67ce69
I have a table where records will be getting inserted every 4 hours on a daily basis. If the record was not inserted for continuous 4 hours, I need to insert a log into another table. Below is the table schema.
Id DocPathid CreatedAt
1 1 2021-04-02 00:00:00
2 1 2021-04-02 04:00:00
3 1 2021-04-02 09:00:00
4 1 2021-04-02 12:00:00
5 1 2021-04-02 16:00:00
6 1 2021-04-02 20:00:00
7 1 2021-04-02 24:00:00
In the above case, there was no records inserted within a interval of 4hours (i.e. between 2021-04-02 04:00:00 & 2021-04-02 09:00:00). The query should return no. of failure count (in this case it is failed for 1 time).
Is there a way to achieve this in MySQL?
You can do something like this.
select count(1)
from (
select id, CreatedAt , timestampdiff(hour, CreatedAt
, lead(CreatedAt,1) over (partition by DocPathid order by CreatedAt) ) as hour
from Table1
) t
where hour >4
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=9b0c631145422dbccd2ea23f0a7d2011
MySQL version 8.0
I want to calculate time difference between two datetime column.
And get rows where duration >= 12:00:00.
which I would normally do:
select id
, start_time
, end_time
, timediff(end_time, start_time) as duration
from table;
which I would get something like this:
id start_time end_time duration
0 1 2020-06-01 01:00:00 2020-06-01 14:00:00 13:00:00
1 2 2020-06-01 01:00:00 2020-06-01 18:00:00 17:00:00
2 3 2020-06-01 19:00:00 2020-06-02 10:00:00 15:00:00
3 4 2020-06-02 04:00:00 2020-06-02 16:00:00 12:00:00
For duration column I don't want times between 00:00:00 ~ 04:00:00 to be added towards the duration. So for the first row duration = 10:00:00 since 01:00:00~14:00:00 = 10:00:00, ignoring times between 00:00:00 ~ 04:00:00
same for second row we substract 3 hours from duration.
so my desired output would be:
id start_time end_time duration
0 1 2020-06-01 01:00:00 2020-06-01 14:00:00 10:00:00
1 2 2020-06-01 01:00:00 2020-06-01 18:00:00 14:00:00
2 3 2020-06-01 19:00:00 2020-06-02 10:00:00 11:00:00
3 4 2020-06-02 04:00:00 2020-06-02 16:00:00 12:00:00
There are lots of rows where times include minutes and seconds too.
Thanks in advance!
I've grabbed all rows where duration >= 12:00:00.
Then separated data into 4 regions depending on their start_time.
a_region = 00~04
b_region = 04~12
c_region = 12~16
d_region = 16~24
For a_region I've subtracted 04:00:00 - start_time which is time we should compensate to duration in a_region.
compensation = 04:00:00 - start_time
compensated_time = duration - compensation.
For b_region it needs no compensation if it has passed 00~04 it means it already passed duration = 12:00:00.
For c_region,
compensation = 16:00:00 - start_time
compensated_time = duration - compensation
For d_region since we've grabbed duration >= 12:00:00
it will pass all of 00~04 therefore
compensated_time = duration - 04:00:00.
I solved it using Python but above is the logic I've used.
One option uses greatest():
select id
, start_time
, end_time
, timediff(
greatest(,
end_time,
date_format(end_time, '%Y-%m-%d 04:00:00')
),
greatest(
start_time,
date_format(start_time, '%Y-%m-%d 04:00:00')
)
) as duration
from table;
MariaDB version 10.4.10.
I have a stock scraper script that fetches stock data every hour and inserts it into a MySQL database. The database structure is similar to this:
stocks( time_fetched DATETIME, fetch_id INT, name VARCHAR, price INT )
And some example data:
**time_fetched fetch_id name price**
2020-03-10 09:00:00 1 stock1 10
2020-03-10 09:00:00 1 stock2 15
2020-03-10 10:00:00 2 stock1 12
2020-03-10 10:00:00 2 stock2 20
2020-03-10 11:00:00 3 stock1 8
2020-03-10 11:00:00 3 stock2 18
I want a way to get price change for each stock between, say, stocks fetched at 09:00 and 10:00, or between 09:00 and 11:00. Something like this (pseudo code):
SELECT *, DIFF( current_price, price_one_hour_ago) AS change_1h, DIFF( current_price, price_two_hours_ago) AS change_2h
Is it possible to do this directly in MySQL? I am using a PHP script to display the data, so I might have to resort to do it in PHP instead.
You can use a CTE to generate prices delayed by 1 or 2 hours, and then compute the changes using those values (potentially using COALESCE to make NULL values into 0):
WITH prices AS (
SELECT time_fetched, name, price,
LAG(price, 1) OVER(PARTITION BY name ORDER BY time_fetched) AS price_1h,
LAG(price, 2) OVER(PARTITION BY name ORDER BY time_fetched) AS price_2h
FROM stocks
)
SELECT time_fetched, name, price,
COALESCE(price - price_1h, 0) AS change_1h,
COALESCE(price - price_2h, 0) AS change_2h
FROM prices
Output:
time_fetched name price change_1h change_2h
2020-03-10 09:00:00 stock1 10 0 0
2020-03-10 10:00:00 stock1 12 2 0
2020-03-10 11:00:00 stock1 8 -4 -2
2020-03-10 09:00:00 stock2 15 0 0
2020-03-10 10:00:00 stock2 20 5 0
2020-03-10 11:00:00 stock2 18 -2 3
Demo on dbfiddle also showing query results without COALESCE.
Need help for MySQL query for retrieving record of class schedule that is conflict in the given time.
Ex.
SchedID StartTime EndTime
1 09:00:00 13:00:00
2 08:30:00 10:00:00
3 11:00:00 15:00:00
4 07:30:00 08:30:00
5 11:30:00 13:00:00
I would like to retrieve the list that is conflict in this given time
Start Time = 09:00:00
End Time = 11:00:00
The record will yield following result:
SchedID StartTime EndTime
1 09:00:00 13:00:00
2 08:30:00 10:00:00
3 11:00:00 15:00:00
Thank you.
Try this
SELECT DISTINCT a.schedid, a.starttime, a.endtime
FROM
tbl_name a, tbl_name b
WHERE ((b.starttime > a.starttime AND b.starttime < a.endtime)
OR (b.endtime > a.starttime AND b.endtime < a.endtime))
ORDER BY schedid
i just found my answer here Determine Whether Two Date Ranges Overlap
Heres my query:
SELECT * FROM tableName WHERE (start_time < givenEndTime)
AND (end_time > givenStartTime);
This works perfectly fine for me.