I need to write a query to select all free slots from appointment table but have problem writing such complex query. Maybe someone could help me I will try to explain what I need to get.
I have table structure:
+---------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| patient_id | int(11) | YES | MUL | NULL | |
| start_time | datetime | NO | | NULL | |
| end_time | datetime | NO | | NULL | |
+---------------+----------+------+-----+---------+----------------+
The problem is that I need to get free slots. For e.g. The works starts at 8:00 AM and ends at 8:00PM and this goes from Monday to Saturday. From last appointment free space should like 1 year in advance. So here is example response that I expect:
+---------------------+---------------------+------------------+
| Free Time Start | Free Time End | Free Time Length |
+---------------------+---------------------+------------------+
| 2014-09-15 08:00:00 | 2014-09-15 08:30:00 | 30 |
| 2014-09-15 09:30:00 | 2014-09-15 12:00:00 | 150 |
| 2014-09-15 15:00:00 | 2014-09-15 20:00:00 | 300 |
| 2014-09-16 09:00:00 | 2014-09-16 11:30:00 | 150 |
| 2014-09-16 13:00:00 | 2014-09-16 17:00:00 | 240 |
| 2014-09-16 18:00:00 | 2015-09-16 18:00:00 | 525600 |
+---------------------+---------------------+------------------+
The sample data for results above:
+----+------------+---------------------+---------------------+
| id | patient_id | start_time | end_time |
+----+------------+---------------------+---------------------+
| 1 | 1 | 2014-09-15 08:30:00 | 2014-09-15 09:30:00 |
| 2 | 1 | 2014-09-15 12:00:00 | 2014-09-15 15:00:00 |
| 3 | 1 | 2014-09-16 08:00:00 | 2014-09-16 09:00:00 |
| 4 | 1 | 2014-09-16 11:30:00 | 2014-09-16 13:00:00 |
| 5 | 1 | 2014-09-16 17:00:00 | 2014-09-16 18:00:00 |
+----+------------+---------------------+---------------------+
So in plain query should:
Get free space 08:00:00 <= x <= 20:00:00 and through monday to saturday (Other days, hours should be skipped)
After last appointment free time range should be 1 year in advance
Sorry if similar questions exists I could not reuse that solutions for hour/day offsets and 1 year in advance... I tried to do this in PHP but it tooks a really long time...
Related
I have this structure:
+--------------------------------------+---------------------+--------+-------------+-------------+
| id | start | User | billedHours | trail90dsum |
+--------------------------------------+---------------------+--------+-------------+-------------+
| 2e88f9f9-2543-11eb-9d57-02b150913215 | 2018-10-04 11:00:00 | User 1 | 15.30 | NULL |
| 2e89af0a-2543-11eb-9d57-02b150913215 | 2018-10-09 12:00:00 | User 1 | 0.40 | NULL |
| 2e8a400b-2543-11eb-9d57-02b150913215 | 2018-10-09 17:00:00 | User 2 | 0.60 | NULL |
| 2e8ae87d-2543-11eb-9d57-02b150913215 | 2018-10-25 17:30:00 | User 2 | 0.30 | NULL |
| 2e8ba472-2543-11eb-9d57-02b150913215 | 2018-10-27 15:00:00 | User 3 | 1.20 | NULL |
| 2e975c93-2543-11eb-9d57-02b150913215 | 2018-10-29 17:30:00 | User 3 | 0.30 | NULL |
| 2e980477-2543-11eb-9d57-02b150913215 | 2018-11-02 13:30:00 | User 1 | 1.90 | NULL |
| 2e98a874-2543-11eb-9d57-02b150913215 | 2018-11-03 12:00:00 | User 2 | 0.70 | NULL |
| 2e993a7d-2543-11eb-9d57-02b150913215 | 2018-11-04 13:30:00 | User 3 | 1.30 | NULL |
| 2ea9fa03-2543-11eb-9d57-02b150913215 | 2018-11-11 11:00:00 | User 1 | 0.90 | NULL |
+--------------------------------------+---------------------+--------+-------------+-------------+
I am looking for an UPDATE query that will fill the trail90dsum field with SUM(billedHours) for each user within the trailing 90 days (as of each start date).
I was able to construct a SELECT query with a join for reporting the data, but I can't transform it to work in the UPDATE syntax.
Thanks,
Phillip
Thanks for all the help. This query got the job done:
UPDATE mytable dst JOIN
(SELECT t1.user, t1.start, t1.billedHours, SUM(t2.billedHours) as last90
FROM mytable as t1 JOIN mytable as t2 ON DATEDIFF(t1.start, t2.start) BETWEEN 0
AND 90 WHERE t1.user=t2.user
GROUP BY t1.user, t1.start, t1.billedHours) src
SET dst.trail90dsum=src.last90
WHERE src.start=dst.start and src.user=dst.user
Thanks, -P
Good morning,
I need a solution for counting days between specific dates. In addition I need to exclude specific dates (public holidays and weekends), which is easy and there are plenty of instructions on how to do it.
But I need to accomplish one more thing. For every person, I also have custom vacation periods and I need them to be subtracted from the previous result.
At the moment, i have two tables. One for the custom vacation periods:
+-----+----------+---------------------+---------------------+------+
| id | employee | start | end | away |
+-----+----------+---------------------+---------------------+------+
| 835 | 2.3 | 2016-12-05 00:00:00 | 2016-12-10 00:00:00 | P |
| 836 | 5.3.5.1 | 2017-01-03 00:00:00 | 2017-01-23 00:00:00 | P |
| 837 | 5.3.5.6 | 2016-12-21 00:00:00 | 2017-04-01 00:00:00 | P |
| 838 | 5.3.3.1 | 2017-01-01 00:00:00 | 2017-01-03 00:00:00 | P |
| 839 | 5.3.1.2 | 2017-01-03 00:00:00 | 2017-01-12 00:00:00 | P |
| 840 | 5.3.1.6 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 | P |
| 841 | 5.1.7 | 2017-01-09 00:00:00 | 2017-01-15 00:00:00 | P |
| 842 | 2.2 | 2017-02-16 00:00:00 | 2017-02-26 00:00:00 | P |
| 843 | 2.5 | 2017-07-31 00:00:00 | 2017-08-06 00:00:00 | P |
| 844 | 2.5 | 2017-08-21 00:00:00 | 2017-08-27 00:00:00 | P |
| 845 | 2.5 | 2017-06-26 00:00:00 | 2017-07-09 00:00:00 | P |
| 846 | 2.4 | 2017-04-04 00:00:00 | 2017-04-08 00:00:00 | P |
+-----+----------+---------------------+---------------------+------+
12 rows in set (0.00 sec)
The reason I have date and time on start and end is that the table actually has info about their working schedule also. The vacation schedule is marked by column 'away' set as 'P'.
The second table looks like this:
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | varchar(45) | YES | | NULL | |
| dayofweek | varchar(45) | YES | | NULL | |
| short | varchar(45) | YES | | NULL | |
| holiday | varchar(45) | YES | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
This table is basically a calendar with additional information about what day of week it is (i know that this can be done with dayofweek() but i need this column also), 'short' determines whether it's a shortened workday (essential when calculating hours because there are a couple of days in a year when the working day is shorter) and 'holiday' which marks public holidays.
I am trying to figure out how to calculate working days during one month (01.08.2017 - 31.08.2017) by subtracting public holidays and weekends (which I can do and is relatively easy) and also subtract custom period of vacations. The main problem is that for some people, there can be more than one period of absence during one month.
If you look at the example above, employee 2.5 has a vacation period that starts in July and ends in August and I need to subtract only the amount in that period that is in August. Employee 2.5 has another vacation in August and I need to subtract this also. In addition to that, this employee has another vacation from June to July.
The perfect outcome would be something like this:
+----------+-------+---------------+--------------+---------------+
| employee | month | working hours | working days | vacation days |
+----------+-------+---------------+--------------+---------------+
The only solution I am able to come up with is to create a new table called vacations with a column for each day and each row shows data for one employee. Or the other way around where employees are columns and rows are dates. But I would not like to start playing with this idea before i know that there isn't an easier way to do it.
Once again, I am sure that there is an easy function in mysql for this but I fail to figure it out.
To calculate working hours do this:
$to_time = strtotime("$row[2]");
$from_time = strtotime("$row[3]");
$diff = $to_time - $from_time
echo round(abs($diff) / 3600,2). " hour(s)";
Where $row[2] & $rows[3] are your start and end dates. They /3600 is for calculating hours, you can get minutes by just writing 60 instead of 3600.
As for the working days and vacations, you can set up a counter and increment it for days and decrement it for vacations using the $diff variable. Count the number of hours or days you like and then increment/decrement it the way you like.
I tidied it up a bit. I finally got what I needed.
At first I changed the calendar table:
mysql> select id,date,workday from calendar where date between '2017-06-20' and '2017-06-30';
+-----+------------+---------+
| id | date | workday |
+-----+------------+---------+
| 202 | 2017-06-20 | 1 |
| 203 | 2017-06-21 | 1 |
| 204 | 2017-06-22 | 0.625 |
| 205 | 2017-06-23 | Holiday |
| 206 | 2017-06-24 | Holiday |
| 207 | 2017-06-25 | NULL |
| 208 | 2017-06-26 | 1 |
| 209 | 2017-06-27 | 1 |
| 210 | 2017-06-28 | 1 |
| 211 | 2017-06-29 | 1 |
| 212 | 2017-06-30 | 1 |
+-----+------------+---------+
11 rows in set (0.00 sec)
1 in workday column says it's an ordinary workday, NULL means that it is not (weekend) and 0.625 is shortened workday (5/8=0,625).a shortened working day is 5 hours. This basically means if i sum the workday column and multiply it with the length of an ordinary workday, I get the normal working hours according to the law between selected period of time.
To do this, at the moment i just set up some variables, this is just a work in progress:
set #START='2016-12-01';
set #END='2016-12-31';
set #WORKDAY=8;
set #norm=(select sum(workday)*#workday from calendar where date between #start and #end);
So the variables #START and #END give me the period i am interested in. #NORM will give me the normal working hours if one is not on vacation or sick leave between #START and #END.
I added some generic data to schedule:
mysql> select id,position,start,end,away from schedule where away='P';
+-----+----------+---------------------+---------------------+------+
| id | position | start | end | away |
+-----+----------+---------------------+---------------------+------+
| 835 | 2.3 | 2016-12-05 00:00:00 | 2016-12-10 00:00:00 | P |
| 836 | 5.3.5.1 | 2017-01-03 00:00:00 | 2017-01-23 00:00:00 | P |
| 837 | 5.3.5.6 | 2016-12-21 00:00:00 | 2017-04-01 00:00:00 | P |
| 838 | 5.3.3.1 | 2017-01-01 00:00:00 | 2017-01-03 00:00:00 | P |
| 839 | 5.3.1.2 | 2017-01-03 00:00:00 | 2017-01-12 00:00:00 | P |
| 840 | 5.3.1.6 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 | P |
| 841 | 5.1.7 | 2017-01-09 00:00:00 | 2017-01-15 00:00:00 | P |
| 842 | 2.2 | 2017-02-16 00:00:00 | 2017-02-26 00:00:00 | P |
| 843 | 2.5 | 2017-07-31 00:00:00 | 2017-08-06 00:00:00 | P |
| 844 | 2.5 | 2017-08-21 00:00:00 | 2017-08-27 00:00:00 | P |
| 845 | 2.5 | 2017-06-26 00:00:00 | 2017-07-09 00:00:00 | P |
| 846 | 2.4 | 2017-04-04 00:00:00 | 2017-04-08 00:00:00 | P |
| 847 | 2.3 | 2017-07-31 00:00:00 | 2017-08-06 00:00:00 | P |
| 848 | 2.3 | 2017-08-21 00:00:00 | 2017-08-27 00:00:00 | P |
| 849 | 2.3 | 2017-06-26 00:00:00 | 2017-07-09 00:00:00 | P |
| 850 | 2.5 | 2017-08-29 00:00:00 | 2017-09-15 00:00:00 | P |
| 851 | 2.3 | 2017-08-29 00:00:00 | 2017-09-15 00:00:00 | P |
| 852 | 2.1 | 2016-11-15 00:00:00 | 2016-12-20 00:00:00 | P |
| 853 | 2.2 | 2016-11-10 00:00:00 | 2017-01-15 00:00:00 | P |
+-----+----------+---------------------+---------------------+------+
19 rows in set (0.00 sec)
And to get the info about each employees specific working I use following select:
SELECT position,#NORM AS normhrs,#NORM - (SUM(vac_workday))*#WORKDAY AS wrkhrs
FROM
(
SELECT position,holiday_start,holiday_end,
(SELECT SUM(workday) FROM calendar WHERE date BETWEEN holiday_start AND holiday_end) AS vac_workday
FROM
(
SELECT position,
DATE_FORMAT(CASE WHEN start<=#START THEN #START ELSE start END,'%Y-%m-%d') AS 'holiday_start',
DATE_FORMAT(CASE WHEN end>=#END THEN #END ELSE end END,'%Y-%m-%d') AS 'holiday_end'
FROM schedule WHERE away IS NOT NULL AND DATE_FORMAT(CASE WHEN start<=#START AND end>=#START THEN #START
WHEN start>=#START AND start<=#END THEN start
ELSE NULL end,'%Y-%m')=DATE_FORMAT(#START,'%Y-%m')
)
AS outertable
)
AS outertable2 GROUP BY position;
which gives me the output. there are only people who have vacations from #START to #END. If the person is not in the output, norm hours apply:
+----------+---------+--------+
| position | normhrs | wrkhrs |
+----------+---------+--------+
| 2.1 | 165 | 53 |
| 2.2 | 165 | 0 |
| 2.3 | 165 | 133 |
| 5.3.5.6 | 165 | 120 |
+----------+---------+--------+
4 rows in set, 6 warnings (0.00 sec)
This is the output i was looking for. i still need to come up with SELECT to sum up the actual working hours but it is easy enough not to put it in here.
Sorry, i know i'm over explaining some stuff but i being total noob, have found similar, rather superthorough answers, very helpful.
What I want to do is to get the smallest scheduled time for each user in a given day. For the examples below I've highlighted just that day's events, which I will filter on my end simply by adding
AND DATE_ADD(scheduled, INTERVAL -$timezone_offset MINUTE)>='$given_day 00:00:00'
AND DATE_ADD(scheduled, INTERVAL -$timezone_offset MINUTE)< '$next_day 00:00:00'
For a simplified example I've got two tables:
checkpoints
+------+---------+----------+---------------------+
| id | type_id | event_id | scheduled |
+------+---------+----------+---------------------+
| 1111 | 9 | 2222 | 2015-06-05 12:00:00 |
| 1150 | 1 | 2222 | 2015-06-05 10:00:00 |
| 1200 | 11 | 3333 | 2015-06-05 23:00:00 |
| 1305 | 1 | 4444 | 2015-06-05 15:00:00 |
| 1400 | 2 | 4444 | 2015-06-05 05:00:00 |
| 1405 | 3 | 4444 | 2015-06-05 19:00:00 |
| 1500 | 1 | 5555 | 2015-06-05 17:00:00 |
| 1505 | 9 | 5555 | 2015-06-05 18:00:00 |
+------+---------+----------+---------------------+
events
+------+---------+---------------------+
| id | user_id | start |
+------+---------+---------------------+
| 2222 | 3 | 2015-06-05 11:00:00 |
| 3333 | 5 | 2015-06-05 23:00:00 |
| 4444 | 2 | 2015-06-05 07:00:00 |
| 5555 | 5 | 2015-06-05 19:00:00 |
+------+---------+---------------------+
I want to query such that I get the following:
+------+---------+---------------------+---------------------+
| id | user_id | start | scheduled |
+------+---------+---------------------+---------------------+
| 1150 | 3 | 2015-06-05 11:00:00 | 2015-06-05 10:00:00 |
| 1500 | 5 | 2015-06-05 19:00:00 | 2015-06-05 17:00:00 |
| 1400 | 2 | 2015-06-05 07:00:00 | 2015-06-05 05:00:00 |
+------+---------+---------------------+---------------------+
That is: the first scheduled time of any given driver's day.
The query I thought would work is:
SELECT
checkpoints.id AS id,
user_id,
start,
MIN(scheduled) AS scheduled
FROM checkpoints
LEFT JOIN events ON event_id=events.id
WHERE DATE_ADD(scheduled, INTERVAL -240 MINUTE)>='2015-06-05 00:00:00'
AND DATE_ADD(scheduled, INTERVAL -240 MINUTE)<'2015-06-06 00:00:00'
AND user_id IS NOT NULL
GROUP BY user_id, DATE(DATE_ADD(scheduled, INTERVAL -240 MINUTE));
The problem is that I'm getting the wrong checkpoint id and start time, even though the user_id and earliest are correct. To illustrate the problem, I see a result like:
+------+---------+---------------------+---------------------+
| id | user_id | start | scheduled |
+------+---------+---------------------+---------------------+
| 1200 | 5 | 2015-06-05 23:00:00 | 2015-06-05 17:00:00 |
+------+---------+---------------------+---------------------+
What am I doing wrong?
Your query would be illegal for most of SQL implementations. MySQL allows to SELECT columns that are neither aggregated nor included in GROUP BY clause.
However, the server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate
I am trying to construct a query that produces one value per day and I want that value to be the last recorded value of the day. This is what my table looks like (actual dataset contains a row for each minute):
+------------+----------+-------+
| Date | Time | Value |
+------------+----------+-------+
| 2015-06-14 | 13:00:00 | 3 |
| 2015-06-14 | 14:00:00 | 5 |
| 2015-06-15 | 11:00:00 | 6 |
| 2015-06-15 | 17:00:00 | 9 |
| 2015-06-16 | 09:00:00 | 2 |
| 2015-06-16 | 10:00:00 | 4 |
+------------+----------+-------+
I would like the outcome of the query to look like this:
+------------+----------+-------+
| Date | Time | Value |
+------------+----------+-------+
| 2015-06-14 | 14:00:00 | 5 |
| 2015-06-15 | 17:00:00 | 9 |
| 2015-06-16 | 10:00:00 | 4 |
+------------+----------+-------+
What would the SQL-statement need to be to retrieve this result? Any help is much appreciated!
select t1.date,
t1.time,
t1.value
from table t1
where t1.time= (select max(t2.time)
from table t2
where t2.date = t1.date);
I have:
Table infoUpdate
+-------+-----------+-----------+-----------+-----------+
| idKey1| idKey2 | __date_Update_ | DayWeek |
+-------+-----------+-----------+-----------+-----------+
| 1___ | __3____ | 2013.01.01 15:00:00 | 2 |
| 1___ | __3____ | 2013.01.01 18:00:00 | 2 |
| 1___ | __3____ | 2013.01.02 15:00:00 | 3 |
| 1___ | __3____ | 2013.01.02 18:00:00 | 3 |
| 1___ | __3____ | 2013.01.03 15:00:00 | 4 |
| 1___ | __3____ | 2013.01.03 18:00:00 | 4 |
+-------+-----------+-----------+-----------+-----------+
How to obtain only previous rows by timeStamp
Select * FROM infoUpdate if where date_Update <='2013.01.03 18:00:00';
This is what I'm looking at obtaining:
+-------+-----------+-----------+-----------+-----------+
| idKey1| idKey2 | date_Update | DayWeek |
+-------+-----------+-----------+-----------+-----------+
| 1 | 3 | 2013.01.01 18:00:00 | 2 |
| 1 | 3 | 2013.01.02 18:00:00 | 3 |
| 1 | 3 | 2013.01.03 18:00:00 | 4 |
+-------+-----------+-----------+-----------+-----------+
will this work for you
Select * FROM infoUpdate where date_Update >= DATE_SUB( '2013.01.03 18:00:00' ,INTERVAL 90 DAY )
this will give you 90 days records previous to the date mentioned
Will it work for you:
Select idKey1, idKey2 , DayWeek, MAX(dateUpdate) as dateUpdate
FROM infoUpdate
where date_Update <='2013.01.03 18:00:00'
GROUP BY idKey1, idKey2 , DayWeek, DATE(dateUpdate);
If you are interested in last week data only, DATE(dateUpdate) can be removed from GROUP BY.