Date between 2 other dates, disregarding year - php

I'm looking for something like the person here was lookin for, only I'd like to use MySQL. The table below is something you'd find in my database (simplified).
+------+------+------+------+
| id | name | first| last |
+------+------+------+------+
| 1 | John | 1020 | 0814 |
| 2 | Ram | 0827 | 0420 |
| 3 | Jack | 0506 | 0120 |
| 4 | Jill | 0405 | 0220 |
| 5 | Zara | 1201 | 1219 |
+------+------+------+------+
First of all the entry has to be random, not id 4 and I only want 1 entry. I worked that out: SELECT * FROM test WHERE id <> 4 ORDER BY rand() LIMIT 1.
In this table the columns 'first' and 'last' are dates formatted as mmdd (both integers). So John is available for most of the year; from October 20th to August 14th. Zara on the other hand is only available for a small period of time; December 1st till December 19th.
My question: how do I change my query to only select the available people? I can't use 'between' since, in John's case, there's nothing between 1020 and 0814.
I just can't figure it out, there's must be other people that have a similar problem... Does anyone have a solution?
Kind regards

You need to distinguish two cases.
When first < last, the dates are in the same year. You can then use between to match dates.
When first > last, it means last is in the next year. In this case, the dates that match are date >= first OR date <= last.
So your WHERE clause should be:
WHERE IF(first < last, #date BETWEEN first AND last,
#date >= first OR date <= last)

Related

MySQL query date range - find common periods

Please accept my apologies for a lack of attempted code.
I can't get my head around this - or even figure out if it's feasible!
I have a table filled with 'date slots', these can be booked by an individual.
The time slots table looks like this:
ID | Price | Available_from | Available_ to
------------------------------------
1 | 20.00 | 2017-10-01 | 2018-01-01
2 | 20.00 | 2017-11-01 | 2017-12-07
3 | 20.00 | 2017-10-31 | 2018-01-31
4 | 20.00 | 2017-10-22 | 2017-11-21
------------------------------------
In these rows, there is a common date range where all four results are available, 2017-11-01 > 2017-11-21
I would like to query my database to see if 1) there is a common range for every result and 2) if there is, get the date range.
Desperate for help!
Thank you
I think this would work :
SELECT MAX(Available_from) as MatchFrom,
MIN(Available_to) as MatchTo
FROM Temporary.test
WHERE
(SELECT MAX(Available_from) FROM Temporary.test)
<=(SELECT MIN(Available_to) FROM Temporary.test)
It will return NULL, if there is no overlapping range.

MySQL - selecting the correct data within the correct week using week beginnings

I am trying to select data, when inserting the data it has an auto insert of the date when submitting. So when data is inserted it inserts the current date.
However, in my table I have week beginnings, so I am trying to select the data inside of that week:
mysql> select * from week;
+---------+------+------------+
| week_id | week | date |
+---------+------+------------+
| 1 | 1 | 2014-12-29 |
| 2 | 2 | 2015-01-05 |
| 3 | 3 | 2015-01-12 |
| 4 | 4 | 2015-01-19 |
| 5 | 5 | 2015-01-26 |
| 6 | 6 | 2015-02-02 |
| 7 | 7 | 2015-02-09 |
| 8 | 8 | 2015-02-16 |
| 9 | 9 | 2015-02-23 |
| 10 | 10 | 2015-03-02 |
| 11 | 11 | 2015-03-09 |
| 12 | 12 | 2015-03-16 |
| 13 | 13 | 2015-03-23 |
| 14 | 14 | 2015-03-30 |
| 15 | 15 | 2015-04-06 |
| 16 | 16 | 2015-04-13 |
| 17 | 17 | 2015-04-20 |
e.g.
select * from table where date='2015-04-06';
However the data will not be selected and presented because the inserted date was 2015-04-10. The only way to retrieve that data is by doing this:
select * from table where date='2015-04-10'; < when the data was inserted
So my question is, is it possible to select that data from that week beginning?
So if I select data from 2015-04-06 it should show data from the range of 2015-04-06 to 2015-04-12, is that possible?
Hopefully I have explained correctly, been a bit tricky to explain let alone try to implement. I can add any more info if needed.
NOTE: I am trying to use this inside of PHP so where the date is I would just use a variable, just thought I would say.
As the week will always end 6 days from the beginning you can use the between operator and the date_add function like this:
(for your specific example):
select *
from table
where date between '2015-04-06' and date_add('2015-04-06', interval 6 day)
And using a php variable:
select *
from table
where date between '$name_of_dt_var' and date_add('$name_of_dt_var', interval 6 day)
You could also compare the week of the date the data was entered with the weeks in the week table using WEEK() function.
Assuming that week is the same value as week(), the:
select t.*
from table t
where week = week('2015-04-10');
Even if the numbers do not match, then presumably you have some base date (such as 2015-01-01 and simple arithmetic would accomplish something very similar).
I have found that the most robust way to do this sort of week processing is to truncate each date in the table (in your example 2015-04-10) to the preceding Monday at midnight. That way you can compute the week of each item by assigning it to the first day of that week.
This little formula returns the preceding Monday given any DATE or DATETIME value.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
For example,
set #datestamp := '2015-04-10'
SELECT FROM_DAYS(TO_DAYS(#datestamp) -MOD(TO_DAYS(#datestamp) -2, 7))
yields the value 2015-04-06.
So, if you have a table called sale you can add up sales by week like this:
SELECT SUM(amount) weekly_amount,
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7)) week_beginning
FROM sale
GROUP BY FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
This is a very convenient way to handle things, because it's robust over end-of-year transitions. The WEEK() function doesn't work quite as well.
If your business rules say that your weeks begin on Sunday rather than Monday, use -1 rather than -2, as follows.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -1, 7))

Select events that take place in given month

I am trying to select events from my database that are taking place in a given month.
I can't seem to figure out how to select those events that started before the given month and end after it.
I know I should probably get the date range between the two dates and see if the given month is inside this range. I would also like to create a column range which has three values based on the range of the event. When the event starts and ends inside the given month this value will be 'fits'. When the event starts in the given month, but extends past it the value is 'extends'. When it starts before the given month, but end in it, it's 'past'. And finally when it started before and goes further the value is 'full'
What I don't know is how to approach the implementation of this. Can all this be done with MySQL, or does the second part need to be implemented in PHP?
Here's an example of the table structure:
| Event_id | Date_from | Date_to |
+----------+----------------------+--------------------+
| 1 | 2014-03-05 19:28:00 | 2014-03-06 09:00:00 |
| 2 | 2014-02-05 00:00:00 | 2014-03-07 10:00:00 |
| 3 | 2014-03-28 15:00:00 | 2014-05-06 00:00:00 |
| 4 | 2014-02-05 00:00:00 | 2014-05-06 00:00:00 |
| 5 | 2014-05-06 19:28:00 | 2014-06-03 19:28:00 |
And the result of the select statement I am trying to achieve should look like this:
| Event_id | Date_from | Date_to | Range |
+----------+----------------------+----------------------+---------+
| 1 | 2014-03-05 19:28:00 | 2014-03-06 09:00:00 | 'fits' |
| 2 | 2014-02-05 00:00:00 | 2014-03-07 10:00:00 | 'past' |
| 3 | 2014-03-28 15:00:00 | 2014-05-06 00:00:00 | 'extends' |
| 4 | 2014-02-05 00:00:00 | 2014-05-06 00:00:00 | 'full' |
I am completely clueless at this point, therefore I will really appreciate any advice and tips on how this could be done. Thank you very much for reading this and for your help.
PS:
Before I realized I need to take in account the events that match the 'full' range criteria I had this:
SELECT * FROM `eventstesting`
WHERE `e_to` < '2014-04-01' OR
`e_from` > '2014-03-01'
Which successfully selects the events that match the 'past', 'fits' and 'extends' criteria, but still doesn't generate the range column.
I have also checked this topic MySQL - Get a list of months on which events are taking place, but it didn't help me
?
SELECT *
, CASE WHEN date_from >= '2014-03-01' AND date_to < '2014-04-01' THEN 'fits'
WHEN date_from < '2014-03-01' AND date_to BETWEEN '2014-03-01' AND '2014-04-01' THEN 'past'
WHEN date_from BETWEEN '2014-03-01' AND '2014-04-01' AND date_to >= '2014-04-01' THEN 'extends'
WHEN date_from <= '2014-03-01' AND date_to >= '2014-04-01' THEN 'full'
END `range`
FROM eventstesting
HAVING `range` IS NOT NULL;
date range selection trick - I think this is what you're looking for:
start < month_end_date
and
end > month_start_date
Basically you select any event that starts before the month ends and ends after the months' start - i.e. it's either partially or fully overlapping with the month.
I'm not sure if this is exactly what you need, but this is how you usually compare date ranges efficiently.
Ah, there's one more thing: if dates can be 'open' (i.e. an event can still be on-going, and the end date is still NULL, that has to be taken care of separately)

MySQL Conditional Select Replace Query

I have found myself in need of replacing certain returned results from a MySQL Select Query. I have the following table and data (simplified for example purposes)
uid | duration | range | unique | stamp
-----------------------------------------------------------------
23 | d | 43 | 1 | 1
24 | d | 65 | 0 | 2
25 | d | 76 | 0 | 3
26 | d | 33 | 0 | 4
27 | d | 44 | 1 | 5
28 | d | 43 | 1 | 6
29 | d | 67 | 0 | 7
30 | d | 88 | 0 | 8
31 | d | 63 | 0 | 9
The stamp column is what I want to do the replace on. Rather than a simple text replace, I was wondering if its possible to run some sort of user defined function on the column and replace it dynamically.
For example If the data returned in the stamp column is a 1, I would like it to replace it with today's timestamp, if it is a 2 then yesterdays timestamp, a 3, the day before yesterdays and so on and so forth.
So my question is, is it possible to point REPLACE to a function that processes the value and then returns what to replace it with. Or if not, is there another way to accomplish this.
I could obviously post process the returned data in PHP and make the changes, but with millions of records returned, it will increase the load time considerably.
EDIT TO MAKE THINGS A BIT CLEARER: I want to replace the stamp column in the data returned from a SELECT Query, I am not storing the data anywhere, or replacing the data in the table. The table will remain unchanged.
Thanks
Absolutely possible:
UPDATE stamps
SET stamp = CURRENT_DATE - INTERVAL stamp - 1 DAY;
Fiddle here. Note that you have to decrement the stamp value by 1 to "minus 0 days" for a stamp of 1. If you remove the - 1, you'll end up storing yesterday's date for stamp values of 1.
UPDATE to answer your question about doing it on SELECT:
SELECT CURRENT_DATE - INTERVAL stamp - 1 DAY
FROM stamps;
Updated Fiddle here
You can use the case statement of mysql
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list] ...
[ELSE statement_list]
END CASE
If you are just trying to retrieve the results, use the following:
SELECT CURDATE() - INTERVAL stamp -1 DAY FROM myTable

MySQL sql query LIKE to get current opening hours

So, in my mysql database I am storing days and hours open in one field, called days, the data is stored in the following format:
[Monday:9:17[Tuesday:9:17[Wednesday:10:18[
As you may've guessed, it goes: [Day:From:Till and brackets are just seperatars for PHP to distinguish how many days are there.
I've been thinking all day what query would be but I could not figure out, so basically I need to get current date and time using PHP:
date(l); // Day in full text representation.
date(G); // current hour in 24 hour format.
So basically I need a query which in simple english would sound like:
SELECT all FROM businessdetails WHERE column date CONTAINS [current date] and :#:# numbers to be less than current hour and greater than current hour.
Help? My brain is melting by now.
So honestly the best thing to do is to normalize your database so you can do better queries. BUT I love to see if I can solve impossible situations so here is what you can do!
This will check all the business that are open on Tuesday at 11am
SELECT * FROM `businessdetails` WHERE `date` REGEXP 'Tuesday:(0|1|2|3|4|5|6|7|8|9|10|11):(11|12|13|14|15|16|17|18|19|20|21|22|23)[^0-9]'
(Funny thing I've found I can't seem to escape the [ in the column so I had to make sure the Regex doesn't have any extra digits at the end or it may erroneously match 2 and 20 or something.)
Here's how you can generate that REGEXP string via PHP:
<?php
$regexp = date('l') . ':(' . join('|', range(0, date('G'))) . '):(' . join('|', range(date('G'), 23)) . ')[^0-9]';
DISCLAIMER I don't actually recommend doing this but I thought it was clever and wanted to share since it directly answers your question.
EDIT
Just noticed you changed your answer. below may not apply anymore, but I'll leave it for future reference...
I would suggest having a separate child table for this.
STORES
auto increment ID
|
| the store name the store description etc..
| / / /
.--------------------------------------------------.
| id | name | description | etc |
|--------------------------------------------------|
| 1 | mary's kitchen | a fancy restaurant | etc |
| 2 | willow creek inn | we serve breakfast | etc |
'--------------------------------------------------'
STORE_HOURS
auto increment ID
| The STORES.id
| / the day (0-SUN, 6-SAT)
| _________/ / the 24h time OPEN (HH:MM:SS *TIME*)
| / _________/ ____/ the 24h time CLOSE (HH:MM:SS *TIME*)
| / / / /
.----------------------------------------------.
| id | store_id | day | time_open | time_close |
| 1 | 1 | 1 | 08:30:00 | 20:00:00 |
| 2 | 1 | 2 | 08:30:00 | 20:00:00 |
| 3 | 1 | 3 | 10:30:00 | 20:00:00 |
| 4 | 1 | 4 | 11:00:00 | 20:00:00 |
| 5 | 1 | 5 | 08:30:00 | 22:30:00 |
'----------------------------------------------'
Now, depending on what you want to display, you could query the table:
SELECT
stores.name AS store_name,
stores.description AS store_description,
store_hours.day AS store_hours_day,
TIME(store_hours.time_open) AS store_open,
TIME(store_hours.time_close) AS store_close
FROM
stores
JOIN
store_hours
ON
store_hours.store_id = stores.id
Result: http://sqlfiddle.com/#!2/e6872/8/0
With this table structure and relationship, you can then create granular queries without too much effort.
So this might be a hell of a response, but here is one way to do it... (Although I'm sure there must be more significantly better ways:
$day = date(l); // Day in full text representation.
$time = date(G); // current hour in 24 hour format.
$sql = "SELECT businessID FROM (SELECT SUBSTRING_INDEX(t_time,':',1) as start, SUBSTRING_INDEX(LEFT(t_time,POSITION('[' IN t_time) - 1), ':',-1) as end,businessID from (SELECT SUBSTRING_INDEX(SUBSTR(`column_date`,POSITION('$day' IN `column_date`) + LENGTH('" . $day . "') + 1),':',2) as t_time, businessID from `businessdetails ` where `column_date` like '%$day%') as t_table_1) as t_table_2 where start >= $time && end <= $time";
Hopefully that works =)
PS If you need help there are all these string functions you could use:
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html

Categories