I have a table lets say:
tblHotel
id
start_date
end_date
rate
Now I want to write procedure for update records for date range, say for example I have data:
id start_date end_date rate
1 2016/01/01 2016/01/10 10
2 2016/01/11 2016/01/20 50
Now if a new date range and rate comes from supplier I want to update tables record like new range is.
start_date end_date rate
2016/01/05 2016/01/12 100
Now updated records should be like this:
id start_date end_date rate
1 2016/01/01 2016/01/04 10
2 2016/01/05 2016/01/12 100
3 2016/01/13 2016/01/20 50
I'm not going to write the code for you, but handling overlapping time frame is tricky. You need to handle this as different cases:
If nothing overlaps, then this is simple:
insert into tbl_Hotel(start_date, end_date, rate)
select $start_date, $end_date, $rate
from dual
where not exists (select 1
from tbl_Hotel h
where h.start_date <= $end_date and h.end_date >= $start_date
);
Easy . . . And in the stored procedure the where can be handled using if logic.
Then the hard part. There are four types of overlaps:
-------hhhhhhhhhhh--------
a) ---------xxxxx------------
b) -----xxxxxx---------------
c) ----------xxxxxx----------
d) --xxxxxxxxxxxxxxxxxxxxxx--
And, then it gets a bit more complicated because a new rate period could overlap with more than one existing period.
Arrrg! How do you approach this? Carefully and with test cases. You might even want to use a cursor (although there are non-cursor-based methods as well).
The idea is to pull out one overlapping existing period. Then, for that period handle the logic:
a) The existing period needs to be split into two parts (with appropriate end dates. Then the new reservation can just be added.
b) The start date of the existing period has to change to one more than the end date of the new one. Then the new one inserted.
c) The end date of the existing period has to change to one less than the start date of the new one. Then the new one inserted.
d) The old record is removed and the new one inserted.
As I say, good tests for your stored procedure are important, so you can actually check that it works.
Related
So I'm working on a schedule system for my job a basically i wanted to know if there is a way where mysql can do something like:
|Monday |tuesday|wendsday|total
|Dan |5am-7am |7am-6pm|6am-11am|
11am-2pm| |2pm-7pm |
5pm-12am|
where i can enter multiple shifts on 1 day for each person in the same cell if needed instead of the name repeating several times like:
Dan|5-4|
Dan|6-8|
and if there is a function to calculate total time in one cell with multiple shifts
There is a way (representing the data as string), but you wouldn't want to do this - you will loose all calculations, searches etc.
You should not try to represent the data in the database exactly as how it looks on paper.
I would make a table like this:
ShiftID|Person|StartTime|EndTime
Making StartTime & EndTime columns of type DATETIME, you will store not only the HH:mm of a shift's start, but also the day. This is helpful when you have a shift which starts on one day and ends in the next, like starting on Monday 2017-05-15 23:00 and ending on Tuesday 2017-05-16 02:00.
You can extract the date only from this filed using MySQL DATE() function and select only those entires which start OR end on this day.
To calculate the shift's duration you can use MySQL function TIMESTAMPDIFF()
You can even use DAYOFWEEK() to get if it is Monday, Tuesday, etc.
About duplicating the person's name - I would make another table, which will match users with their data to IDs an use ID in the column Person, but for a starter and if your data is not big and if speed is not an issue and if typo errors (like Den instead of Dan) are not a problem ... you could use the name directly in this table.
After storing the data in a table like this you could represent it as you wish in HTML (or print).
You can create a third table with the following columns:
person_id int,
start_time datetime,
end_time datetime
Where person_id would be foreign key to Person table and start_time and end_time would be datetime columns. You can then store multiple records for a person in this table and use MySQL's date functions with GROUP BY to generate the report similar to the one in question.
I have a mysql table which stores users' availability, stored in 'start' and 'end' columns as date fields.
I have a form where other users can search through the 'availabilty' with various periods like, today, tomorrow and next week . I'm trying to figure out how to construct the query to get all the rows for users who are available 'next month'.
The 'start' values maybe from today and the 'end' value might might be three months away but if next month falls between 'start' and 'end' then I would want that row returned.
The nearest I can get is with the query below but that just returns rows where 'start' falls within next month. Many thanks,
sql= "SELECT * FROM mytable WHERE start BETWEEN DATE_SUB(LAST_DAY(DATE_ADD(NOW(), INTERVAL 1 MONTH)),INTERVAL DAY(LAST_DAY(DATE_ADD(NOW(), INTERVAL 1 MONTH)))-1 DAY) AND LAST_DAY(DATE_ADD(NOW(), INTERVAL 1 MONTH))";
As you are interested in anything that happens in the full month following the current date you could try something like this:
SELECT * FROM mytable WHERE
FLOOR(start/100000000)<=FLOOR(NOW()/100000000)+1 AND
FLOOR( end/100000000)>=FLOOR(NOW()/100000000)+1
This query make use of the fact that datetime values are stored in MySql internally as a number like
SELECT now()+0
--> 20150906130640
where the digits 09 refer to the current month. FLOOR(NOW()/100000000) filters out the first digits of the number (in this case:201509). The WHERE conditions now simply test whether the start date is anywhere before the end of the next month and the end date is at least in or after the period of the next month.
(In my version I purposely left out the condition that start needs to be "after today", since a period that has started earlier seems in my eyes still applicable for your described purpose. If, however, you wanted that condition included too you could simply add an AND start > now() at the end of your WHERE clause.)
Edit
As your SQLfiddle is set-up with a date instead of a (as I was assuming) datetime column your dates will be represented differently in mumeric format like 20150907 and a simple division by 100 will now get you the desired month-number for comparison (201509):
SELECT * FROM mytable WHERE
FLOOR(start/100)<=FLOOR(NOW()/100000000)+1 AND
FLOOR( end/100)>=FLOOR(NOW()/100000000)+1
The number returned by NOW() is still a 14-digit figure and needs to be divided by 100000000. See your updated fiddle here: SQLfiddle
I also added another record ('Charlie') which does not fulfill your requirements.
Update
To better accommodate change-of-year scenarios I updated my SqlFiddle. The where clause is now based on 12*YEAR(..)+MONTH(..) type functions.
I have attendance data for employees stored in the table attendance with the following column names:
emp_id (employee ID)
date
type (leave, absent, etc.)
(there are others but I'm omitting them for the sake of simplicity)
My objective is to retrieve all dates of the given month on which the employee was on leave (type = 'Leave') and the last leave taken in the last month, if any.
It's easy to do it using two queries (I'm using PHP to get process the data), but is there any way this can be done in a single query?
I'm answering my own question so as to close it. As #bpgergo pointed out in the comments, UNION will do the trick here.
SELECT * FROM table_name
WHERE type="Leave" AND
date <= (CURRENT_DATE() - 30)
Select the fields, etc you want then se a combined where clause using mysql's CURRENT_DATE() function. I subtracted 30 for 30 days in a month.
If date is a date column, this will return everyone who left 1 month or longer ago.
Edit:
If you want a specific date, change the 2nd month like this:
date <= (date_number - 30)
I have a scenario where I want to be able to SELECT rows from a MySQL table, but exclude rows where the current time-of-day is inside a time-range.
Example:
The "quiet" period for one row is 10pm - 8:30am.
My SQL SELECT statement should not return that row if the current server time is after 10pm or before 8:30am.
Example 2: The "quiet period" is NULL and ignored.
Example 3: A new row is created with a quiet period from 9:53am to 9:55am. If the current server time is in that 2-minute window, the row is not returned by the SELECT.
My question:
What data format would you use in the database, and how would you write the query?
I have thought about a few different approaches (defining start_time as one column and duration as another, defining both in seconds... or using Date stamps... or whatever). None of them seem ideal and require a lot of calculation.
Thanks!
I would store the start and end dates as MySQL native TIME fields.
You would need to consider ranges that span midnight as two separate ranges but you would be able to query the table like this, To find all current quiet periods
SELECT DISTINCT name FROM `quiet_periods`
WHERE start_time<=CURTIME() AND CURTIME()<=end_time
Or to find all non-active quiet periods
SELECT name FROM quiet_periods WHERE name NOT IN (
SELECT name FROM `quiet_periods`
WHERE start_time<=CURTIME() AND CURTIME()<=end_time
)
So with sample data
id -- name -- start_time -- end_time
1 -- late_night -- 00:00:00 -- 08:30:00
2 -- late_night -- 22:00:00 -- 23:59:59
3 -- null_period -- NULL -- NULL
4 -- nearly_10am -- 09:53:00 -- 09:55:00
At 11pm this would return
null_period
nearly_10am
from the second query.
Depending on performance and how many rows you had you might want to refactor the second query into a JOIN and probably add the relevant INDEXes too.
I have the following table in MySQL that records event counts of stuff happening each day
event_date event_count
2011-05-03 21
2011-05-04 12
2011-05-05 12
I want to be able to query this efficiently by date range AND by day of week. For example - "What is the event_count on Tuesdays in May?"
Currently the event_date field is a date type. Are there any functions in MySQL that let me query this column by day of week, or should I add another column to the table to store the day of week?
The table will hold hundreds of thousands of rows, so given a choice I'll choose the most efficient solution (as opposed to most simple).
Use DAYOFWEEK in your query, something like:
SELECT * FROM mytable WHERE MONTH(event_date) = 5 AND DAYOFWEEK(event_date) = 7;
This will find all info for Saturdays in May.
To get the fastest reads store a denormalized field that is the day of the week (and whatever else you need). That way you can index columns and avoid full table scans.
Just try the above first to see if it suits your needs and if it doesn't, add some extra columns and store the data on write. Just watch out for update anomalies (make sure you update the day_of_week column if you change event_date).
Note that the denormalized fields will increase the time taken to do writes, increase calculations on write, and take up more space. Make sure you really need the benefit and can measure that it helps you.
Check DAYOFWEEK() function
If you want textual representation of day of week - use DAYNAME() function.