Using mysql to query dates that fall between 2 other dates - mysql

I have a table that has a field 'start_date' and also a field 'end_date'. Items are considered active when the current date is between 'start_date' and 'end_date'. How would I find all the items that would be considered active for the next 30 days.

Assuming start_date and end_date are DATETIME, and you want to compare the date AND time components, you could do something like this:
SELECT a.*
FROM atable a
WHERE a.start_date >= NOW()
AND a.end_date <= NOW() + INTERVAL 30 DAYS
If start_date and end_date are DATE (rather than DATETIME), you probably only want to compare the DATE portion (without regard to a time component)
SELECT a.*
FROM atable a
WHERE a.start_date >= DATE(NOW())
AND a.end_date <= DATE(NOW()) + INTERVAL 30 DAYS
You may actually want a less than comparison (rather than less than or equal to) on the end date. You need to determine what results you want on that edge case. And the interval may need to be specified as 29 days, depending on how you define "the next 30 days".
Q: What about items that started before NOW and will run for the next 30 days?
A: I think I understand what you were asking.
I think you are wanting to retrieve any rows where the any point in time between the start_date and end_date falls anytime between now and 30 days from now. I can see that my query above does not answer that question.
For now, I'm going to consider only cases where " start_date < end_date ", and shelve cases where start_date is not less than end_date, or where either start_date or end_date or both are NULL.
We can depict the possible cases something (rather crudely) like this:
The vertical bars represent NOW and NOW+30
The less than sign represents "start_date"
The greater than sign represents "end_date"
The dashes represent the range of DATETIME betwen start_date and end_date
NOW NOW+30
<---> | | case 1: end_date < NOW()
<---|----> | case 2: NOW() between start_date and end_date
<--|-------|----> case 3: NOW() > start_date and NOW+30 < end_date
| <---> | case 4: both start_date and end_date between NOW() and NOW()+30
| <--|---> case 5: start_date between NOW() and NOW()+30 and end_date > NOW+30
| | <---> case 6: start_date > NOW()+30
<---> | case e1: end_date = NOW()
<--|-------> case e2: start_date > NOW() AND end_date = NOW()+30
<---> | case e3: start_date = NOW() and end_date < NOW()+30
<-------> case e4: start_date = NOW() and end_date = NOW()+30
<-------|--> case e5: start_date = NOW() and end_date > NOW()+30
| <---> case e6: start_date > NOW() AND end_date = NOW()+30
| <---> case e7: start_date = NOW()+30
I think you are asking to return rows that satisfy cases 2 thru 5, which is all cases EXCEPT for case 1 and 6.
If we can write a predicate that tests for cases 1 and 6, and then negate that, it should give us what you want. Something like this:
To handle datetime with time component considered:
WHERE NOT ( end_date < NOW() OR start_date > NOW() + INTERVAL 30 DAYS )
if start_date and end_date are DATE, to compare just the DATE portion, wrap the NOW() function in the DATE() function:
WHERE NOT ( end_date < DATE(NOW()) OR start_date > DATE(NOW()) + INTERVAL 30 DAYS )
I shelved the oddball cases, of start_date > end_date, start_date = end_date, start_date IS NULL, end_date IS NULL, etc. I also omitted discussion of the edge cases (e1 thru e7), e.g. start_date = NOW(), end_date = NOW(), etc. For completeness, you probably want to check whether those cases are handled appropriately with this same predicate.
DOH!
It just dawned on me that this:
WHERE NOT ( a < n OR b > t )
is equivalent to this (at least for not null values)
WHERE ( a >= n AND b <= t )
So this predicate should be equivalent:
WHERE end_date >= NOW() AND start_date <= NOW() + INTERVAL 30 DAYS

Related

How to order by dates with time periods with current date first then future time periods and then past time periods?

I have a table with columns ID, START_DATE and END_DATE. What I want to do is to execute a query which orders the resulting rows in the following order:
If the current date is 2020-12-14 then the result should be
START_DATE END_DATE
2020-12-8 2020-12-18 -----> Time periods which contains current day and are ordered by start_date in ASCENDING order.
2020-12-9 2020-12-15
2020-12-10 2020-12-17
2020-12-15 2020-12-17 -----> Time periods with START DATE > current date and are ordered by start_date in ASCENDING order.
2020-12-16 2020-12-22
2020-12-21 2020-12-25
2020-12-9 2020-12-13 ----->Time periods with END DATE < current date and are ordered by start_date in DESCENDING order.
2020-12-6 2020-12-8
2020-12-1 2020-12-9
How can I accomplish this?
You need in something similar to
ORDER BY CASE WHEN CURRENT_DATE BETWEEN start_date AND end_date
THEN 1
WHEN start_date > CURRENT_DATE
THEN 2
ELSE 3 END,
CASE WHEN end_date > CURRENT_DATE
THEN start_date + 0
ELSE DATEDIFF(CURRENT_DATE, start_date)
END
fiddle
start_date + 0 needed for to convert the result of this CASE to numeric. Without it the CASE alternatives combines date and numeric values which causes the final datatype to be a string - with wrong sorting order.
You can use conditional ordering as follows:
Select * from your_table
Order by
case when CURRENT_DATE between start_date and end_date OR start_date > CURRENT_DATE
then 1
When end_date < CURRENT_DATE
then 2
end,
case when CURRENT_DATE between start_date and end_date or start_date > CURRENT_DATE
then date_diff(start_date, CURRENT_DATE)
When end_date < CURRENT_DATE
then date_diff(CURRENT_DATE,start_date)
end
In MySQL, I would write this as:
order by (curdate() between start_date and end_date) desc,
(curdate() > curdate()) desc
case expressions are not needed because MySQL treats boolean values as a number, with 1 for true and 0 for false.

Better way of selecting rows where a month falls between start and end date

Suppose the month to be considered is Oct 2013 and I want to pull all rows in which October 2013 falls between start and end date.
I'm currently using this in my WHERE clause:
WHERE (start_date >= '2013-10-01' AND end_date < '2013-11-01') OR // between 1st and 31st
(start_date >= '2013-10-01' AND start_date < '2013-11-01') OR // start falls in oct'13
(end_date >= '2013-10-01' AND end_date < '2013-11-01') // end falls in oct'13
This is almost doing the job expect in the case where the start and end dates are outside Oct'13. For example, it will miss the rows where the start date is say 2013-05-01 and end date is 2014-02-22
I'm sure there would be a short and pretty way to use in the WHERE than the long approach that I'm following at present. Any suggestions?
what about WHERE start_date < '2013-11-01' And end_date >= '2013-10-01'
Add one more OR
WHERE (start_date >= '2013-10-01' AND end_date <= '2013-10-31') OR
(start_date < '2013-10-01' AND end_date > '2013-10-31')

comparing dates by month and year in mysql

I have a table containing data about events and festivals with following columns recording their start and end dates.
Start_Date
End_Date
date format is in YYYY-MM-DD. I need to fetch event details with the following condition.
Need to fetch all events which start with a current month and there end dates can be anything say currentDate+next30days.
I am clear about end date concept. but not sure how I can fetch data whose start dates are in a current month.
For this, I need to compare current year and current month against the Start_Date column in my database.
Can anyone help me to point out as how I can do that?
select * from your_table
where year(Start_Date) = year(curdate())
and month(Start_Date) = month(curdate())
and end_date <= curdate() + interval 30 day
I don't like either of the other two answers, because they do not let the optimizer use an index on start_date. For that, the functions need to be on the current date side.
So, I would go for:
where start_date >= date_add(curdate(), interval 1 - day(curdate()) day) and
start_date < date_add(date_add(curdate(), interval 1 - day(curdate()) day), interval 1 month)
All the date functions are on curdate(), which does not affect the ability of MySQL to use an index in this case.
You can also include the condition on end_date:
where (start_date >= date_add(curdate(), interval 1 - day(curdate()) day) and
start_date < date_add(date_add(curdate(), interval 1 - day(curdate()) day), interval 1 month)
) and
end_date <= date_add(curdate(), interval 30 day)
This can still take advantage of an index.
DateTime functions are your friends:
SELECT
*
FROM
`event`
WHERE
(MONTH(NOW()) = MONTH(`Start_Date`))
AND
(`End_Date` <= (NOW() + INTERVAL 30 DAY))
AND
(YEAR(NOW()) = YEAR(`Start_Date`))
Comparing the year and month separately feels messy. I like to contain it in one line. I doubt it will make a noticeable difference in performance, so its purely personal preference.
select * from your_table
where LAST_DAY(Start_Date) = LAST_DAY(curdate())
and end_date <= curdate() + interval 30 day
So all I'm doing is using the last_day function to check the last day of the month of each date and then comparing this common denominator. You could also use
where DATE_FORMAT(Start_Date ,'%Y-%m-01') = DATE_FORMAT(curdate(),'%Y-%m-01')

Is Today Between Two (DATETIME) Columns? (empty or all zeroes count as unbound upper/lower limit)

I have a table of products and two DATETIME columns, one for start_date and one for end_date.
How do I check if a specific product ID is between the start date and end date? However, if one or both of these is NULL (the default value) accept this lower/upper bound to be unlimited and return the product if it's still within the other bound (if it's set). If both bounds are set to NULL always return the product.
select *
from your_table
where
product_id = 1 AND
(
(CURDATE() between start_date and end_date)
or (CURDATE() >= start_date and end_date is null)
or (CURDATE() <= end_date and start_date is null)
or (start_date is null and end_date is null)
)
select case when ('2012-06-26' between start_date and end_date)
or ('2012-06-26' >= start_date and end_date is null)
or ('2012-06-26' <= end_date and start_date is null)
then 1
else 0
end as is_between_dates
from your_table
where product_id = 1

MySQL, get data between start and end date columns?

I've read a few similar questions then mine, where I could find queries that were pretty much the same I'm using. But I had to ask, because I would like to understand why this is not working:
I have the following data:
id category_id start_date end_date image campaign_id published
1 1 2011-07-05 2011-07-5 a.gif 3 1
2 1 2011-07-01 2011-07-15 c.gif 3 1
3 37 2011-07-01 2011-07-04 d.gif 3 1
I expect to get rows 1 and 2, from this query:
SELECT id, category_id, start_date, end_date FROM categories_campaigns WHERE start_date <= NOW() AND end_date >= NOW();
From what I've experienced, the only row returned is the second. Also this one, gives me the second,
SELECT category_id, start_date, end_date FROM categories_campaigns WHERE end_date >= NOW();
The next one returns me all 3,
SELECT category_id, start_date, end_date FROM categories_campaigns WHERE start_date <= NOW();
The datatype for both columns are DATE. So, my question is, how to solve this ? Why is this happening ? Or I've got an obvious error that I'm not finding on what to look for.
Working query
SELECT
category_id, start_date, end_date
FROM
categories_campaigns
WHERE
start_date <= DATE(NOW()) and end_date >= DATE(NOW());
I think you could use this:
SELECT category_id, start_date, end_date FROM categories_campaigns WHERE left(now(),10) between start_date and end_date;
I suppose start_date and end_date are of date datatype. But NOW() returns a date-time value.
Use CUR_DATE() instead of NOW()