Is there a simple way of using BETWEEN but not including the two dates you want to check between? I noticed if you use BETWEEN it includes the start and end date itself. I've been working on a booking system and want to stop guests booking dates in between another booking or overlapping another booking.
E.g. if there's a booking for 20th May until the 25th May I don't want anyone to be able to book those dates regardless of the start and end date of the new booking. I do, however, want users to be able to have their end date on the same day as a different booking's start date or the start date on the same day as a different booking's end date.
Originally I had:
SELECT id
FROM table
WHERE (? BETWEEN arrive_date AND depart_date)
OR (? BETWEEN arrive_date AND depart_date)
The first ? is for the new arrive date and the second ? is for the new end date.
But when checking it included the start and end date then I tried to work out the correct formula and got this:
SELECT id FROM table
WHERE (? BETWEEN arrive_date - INTERVAL 1 DAY AND depart_date - INTERVAL 1 DAY)
OR (? BETWEEN arrive_date + INTERVAL 1 DAY AND depart_date - INTERVAL 1 DAY)
It seemed to be fine but then I noticed if I book, for example, 9th November and 10th November I can then also book 6th 15th November.
I thought what I had was correct and I am still working it all out but if someone can see where I am going wrong that would be helpful!
If there is a row in the table with arrive_date less than the queried departure date and depart_date greater than the queried arrival date then you will get as result 'booked', else you will get 'free':
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM `table`
WHERE :depart_date > arrive_date AND :arrive_date < depart_date
) THEN 'booked'
ELSE 'free'
END result
Replace :arrive_date and :depart_date with the dates that you will pass as parameters.
See the demo.
Related
SELECT DATE(STR_TO_DATE(CONCAT(CONCAT(YEAR('$uDate1'), week), ' Monday'), '%X%V %W') +
INTERVAL (7 - DAYOFWEEK(STR_TO_DATE(CONCAT(CONCAT(YEAR('$uDate1'), week), ' Monday'),
'%X%V %W'))) DAY)
as week_end_date
What this statement does is take the date I give it ($uDate1) and give me the week end date (Saturday) of that week. This works well and I am happy with it, kinda.
I was wondering if there were some things I missed that would either make this more efficient or even if I missed some shortcuts to this.
Any suggestions for me?
week >= WEEK('$uDate1') AND week <= WEEK('$uDate2')
This is in my WHERE clause. So basically if I use this...
DATE('$uDate1', INTERVAL 7 - DAYOFWEEK('$uDate1') DAY)
...then it returns the same day for all records. I need it to be able to go over a span of a few weeks.
I have a column in my database named 'week'. It simply stores an INT that corresponds to the week of the year. (ex. 21 for this week)
I then have two date picker boxes. The output gets the week end date based of each week that is BETWEEN and INCLUDES the days chosen.
5/10/2016 & 5/26/2016 outputs 5/14/2016, 5/21/2016, 5/28/2016
What gets exported to CSV file looks something like this..
WEEK END, LAST NAME, FIRST NAME, ...
5/10/2016, Smith, John, ...
5/26/2016, Jones, James, ...
It outputs anyone who had hours during the week, with the week end date.
SIDE NOTE: I do appreciate the comments and help. I don't want anyone to stress over this though! Just curious if better way. :)
I am not sure why your current SQL is so complicated.
You say it is just to take a date and give me the week end date (Saturday) of that week .
How you are doing this at the moment is:-
Yours is taking the year
Adding the week of the year (I assume - should be WEEK('$uDate1') I think)
Adding on the day as a string (so for example for today it would be 2016 21 Monday )
Changing that string back to a date a datetime value
Converting that datetime value back to a date.
Then taking the year again
Adding the week of the year again
Getting the day of the week of the resulting string. As you have concatenated Monday on to the date then the day of the week will always be 2.
Taking that resulting day of the week and subtracting it from 7. As the day of the week will always be 2 this will always result in 5
Adding on the day as a string (so for example for today it would be 2016 21 Monday ).
This value is then added on to the previously calculated date, taking the Monday date and adding 5 days.
My suggestion was to just use:-
DATE_ADD($uDate1, INTERVAL 7 - DAYOFWEEK($uDate1) DAY)
which is far simpler, and appears to cover your requirements.
EDIT
Looking at your edit you want a list of all the Saturdays for weeks all or partially between 2 passed dates.
If so I think the following will do it and hopefully be more efficient as there is no need to translate dates to and from string. Note it relies on your week table to add to the date, hence only copes with date ranges of up to that many weeks.
SELECT DATE_ADD(DATE_ADD('$uDate1', INTERVAL 7 - DAYOFWEEK('$uDate1') DAY), INTERVAL `week` WEEK) AS aDate
FROM `week`
HAVING aDate BETWEEN '$uDate1' AND DATE_ADD('$uDate2', INTERVAL 7 - DAYOFWEEK('$uDate2') DAY)
ORDER BY aDate
As I mentioned in comment you should move this transformation from mysql query to php code.
I see no reason to do this calculation on mysql side.
http://ideone.com/48zLvF
$week_day = intval(date('w',$uDate1));
if ($week_day<6) {
$end_of_week = $uDate1+(86400*(6-$week_day));
} else {
$end_of_week = $uDate1;
}
I currently have a query that is getting the records where their deadline is less than 3 months away. The deadline is stored as a Date, but the year is not important as this record should flag up every year.
My query:
SELECT client_name
FROM client
WHERE MOD(DAYOFYEAR(deadline) - DAYOFYEAR(CURDATE()), +365) <= 90
AND DAYOFYEAR(deadline) > DAYOFYEAR(CURDATE())
Apologies if there’s errors in the syntax as I’m writing this from memory – but it does work.
It works up until a deadline is in the first quarter and the current date is in the final quarter then it no longer returns the record. How do I get around this?
So the query needs to return the records that have a deadline within 3 months of the current date. The Year in the deadline date should be ignored as this could be years ago, but it is the day and month of the deadline that is important.
Or is the problem the date I am storing? Should I update this each year?
Thanks
One approach is to use a conditional test like this:
WHERE CONCAT(YEAR(NOW()),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(NOW()),DATE_FORMAT(d.deadline,'-%m-%d'))<NOW() YEAR
< NOW() + INTERVAL 3 MONTH
We can unpack that a little bit. On that first line, we're creating a "next due" deadline date, by taking the current year, and appending the month and day value from the deadline.
But there's a problem. Some of those "next due" deadline dates are in the past. So, to handle that problem (when the 3 month period "wraps" into the next year), we need to add a year to any "next due" deadline date that's before the current date.
Now, we can compare that to a date 3 months from now, to determine if the "next due" deadline date is in the next 3 months.
That's a bit complicated.
Here's a SQL Fiddle as a demonstration: http://sqlfiddle.com/#!2/c90e9/3.
For testing, NOW() is inconvenient because it always returns today's date. So, for testing, we replace all occurrences of NOW() with a user-defined variable #now, and set that to various dates, so we can appropriately test.
Here's the SQL statement I used for testing. The first expression is the conditional test we're planning on using in the WHERE clause. For testing, we want to return all the rows, and just see which rows get due_in_3mo flagged as TRUE (1) and which get flagged as FALSE (0).
The second expression in the SELECT list just the "next due" deadline date, same as used in the first expression.
The rest of the expressions are pretty self-explanatory... we also want to display the date 3 months in the future we're comparing to, and the original "deadline" date value.
SELECT
CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))<#now YEAR
< #now + INTERVAL 3 MONTH
AS `due_in_3mo`
, CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))<#now YEAR
AS `next_due`
, d.id
, d.deadline + INTERVAL 0 DAY AS `deadline`
, #now + INTERVAL 3 MONTH AS `now+3mo`
, #now + INTERVAL 0 DAY AS `now`
FROM d d
CROSS
JOIN (SELECT #now := '2015-11-01') i
ORDER BY d.id
Change the value assigned to #now in the inline view (aliased as i) to test with other date values.
(You may want to use DATE(NOW()) in place of NOW() so that times don't get mixed in, and you may want to subtract another day from that, that really just depends how you want to handle the edge case of a deadline with month and day the same as the current date. (i.e. do you want to handle that as "in the past" or not.)
To summarize the approach: generate the "next due" deadline date as a DATE value in the future, and compare to the a date 3 months from now.
I am using the week function like so:
SELECT
sub.id AS ARID,
WEEK(my.data) AS Week,
Is it possible to add the week beginning date that the week corresponds to?
Something like:
SELECT
sub.id AS ARID,
WEEK(my.data) AS Week,
MIN(my.data) AS WKBeginning
Using MIN in this way I remove a lot of data unintentionally - it should be divorced fromt he data even. Put another way, I'd like to display Week as a date rather than a number that is along the lines 'Monday-2014-09-04'
Is that possible?
My first answer turned out to be incorrect if the source table didn't include the full range of dates for the week (specifically the first day of the week).
The correct way is to use ADDDATE():
ADDDATE(my.data, INTERVAL 1-DAYOFWEEK(my.data) DAY) AS CorrectWKBeginning,
A correlated sub-query should does not work when the first day of the week is not included in the data:
SELECT
id,
WEEK(d) AS Week,
ADDDATE(d, INTERVAL 1-DAYOFWEEK(d) DAY) AS CorrectWKBeginning,
(SELECT MIN(d) FROM t WHERE WEEK(d) = WEEK(t2.d)) as IncorrectWKBeginning
FROM t t2
Sample SQL Fiddle
You might have to use week() with parameters to set the start day of the week and if it counts from 0-53 or 1-53, see the MySQL manual for reference.
Trying to select a closest previous and next holiday from the database. Say, New Year's Day is always at the 1st of January, and New Year's Eve is at the 31st of December. Current year is completely irrelevant, so I'm trying to select previous holiday by day and month (New Year's Eve) with the following MySQL query:
SELECT * FROM `calendar` WHERE DATE_FORMAT(`holidayDate`, "%m-%d") < "01-01"
It gives NULL. I was expecting that it would drop through and will look in the previous month, December, but...
Tried a lot of different ways of doing it, but still no success.
P.S.: Cannot use TIMESTAMP in this case...
The reason it's returning null is because there isn't anything less than '01-01'. The query doesn't wrap around to the beginning.
What I would do is write a case statement that checks to see if you are at the earliest holiday.
If you are the earliest holiday, then you can select the latest holiday (a way of wrapping around).
If you are not the earliest holiday, then you need to select the one before it. I did this by ordering them in descending date, and limiting it to 1. (Effectively grabbing the holiday occurring before the current date.)
Try this:
SELECT *
FROM calendar
WHERE
CASE WHEN DATE_FORMAT(CURDATE(), '%m-%d') = DATE_FORMAT((SELECT MIN(c.holidayDate) FROM calendar c), '%m-%d')
THEN holidayDate = (SELECT MAX(c.holidayDate) FROM calendar c)
ELSE
DATE_FORMAT(holidayDate, '%m-%d') < DATE_FORMAT(CURDATE(), '%m-%d')
END
ORDER BY holidayDate DESC
LIMIT 1;
Here is an SQL Fiddle example. I created two queries. One that uses the current date (seen above) and one that has Jan 1st hard coded to show that the case statement does work. I've only added certain holidays to test.
If two queries are acceptable for you:
SELECT max(holidayDate) as prev_holiday from calendar where holidayDate < now();
SELECT min(holidayDate) as next_holiday from calendar where holidayDate > now();
I have a table "task_table" containing columns-
Task_id, Start_date, End_date
And I have one more "configuration" table which has the records that tell which days of the week are working days.
This table has two columns -
week_day, isHoliday
and this table contains seven records as week_days are the Monday,Tuesday.....Sunday , and each record has an entry as 1 or 0. If a day is a holiday in any organization then there will be 0 against that day. Like if an organisation has holidays on Wednesday and Friday every week then there will be 0 against Wednesday and Friday only.
Now I want to make a SQL query to get the Task_id, Start_date, End_date, and the count of total days consumed on each task. (These days are the days between task start_date and end_date excluding the holiday days as configured in "configuration" table.)
I don't have time to fully answer this question now, but what I would do is:
Get the date as at the start of the Start_date week, and the date as at the end of the End_date week (you can get this by date_adding an amount of days according to the day of the week.
Then you want to date diff them, divide by seven, multiply by two, and remove any that you would have added (e.g. if the start date was Thursday then you'll need to remove one from the result, as you will have counted one for the Wednesday immediately prior.
I'll write the code out tomorrow (it's late here - something like 14 hours from now or so.) if noone else has suggested a better answer.
Edit: Right, didn't properly read the question, but the tactic still applies with a little fiddling. Speaking of which, here is the fiddle of my solution.
It boils down to the following code:
set #holidaysPerWeek = (select sum(isHoliday) from configuration);
select
Task_id,
((dateDiff(
DATE_ADD(End_Date, INTERVAL 7 - DayOfWeek(End_Date) DAY),
DATE_ADD(Start_Date, INTERVAL -DayOfWeek(Start_Date) + 1 Day)) + 1) / 7)
* #holidaysPerWeek
- (select sum(isHoliday) from configuration where week_day > DayOfWeek(End_Date))
- (select sum(isHoliday) from configuration where week_day < DayOfWeek(Start_Date)),
DayOfWeek(End_Date)
from task_table
This does exactly what I was saying before, but with a variable number of "weekends" spread throughout the week, by first selecting the number of holidays for if the full weeks were covered, then removing holidays that were before or after the start and end dates respectively.